Laravel 5: маршруты API + маршрут с подстановочными знаками приводят к неожиданному поведению

Я пытаюсь создать приложение React с поддержкой Laravel API, поэтому, по сути, использую маршрут с подстановочными знаками для маршрутизации на стороне клиента, а затем просто группу маршрутов API для обработки данных.

Это мой routes/web.php файл:

<?php

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/payment/redirect/{orderId}', ['as' => 'mollie.redirect', 'uses' => 'Controller@index']);
Route::get('/{any}', ['as' => 'index', 'uses' => 'Controller@index'])->where('any', '.*');

А это мой routes/api.php файл:

<?php

use Illuminate\Http\Request;

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/

Route::post('/orders', ['as' => 'orders.store', 'uses' => 'OrdersController@store']);
Route::post('/payment/webhook', ['as' => 'mollie.webhook', 'uses' => 'OrdersController@webhook']);

что приводит к:

введите описание изображения здесь

Но всякий раз, когда я пытаюсь сделать запрос на POST api/orders, в Postman я получаю следующее:

введите описание изображения здесь

Это должен быть ответ Controller@index, а не OrdersController@store, который должен быть ответом JSON.

Это мой OrdersController код:

<?php 

namespace Http\Controllers;

use Customer;
use Http\Requests\OrderCreateRequest;
use Order;
use Product;
use Services\CountryDetector;
use Services\LanguageService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Route;

class OrdersController extends Controller
{
    const ERROR_PRODUCT_COUNTRY_UNAVAIALBLE = 'errors.products.country_unavailable';

    public function store(OrderCreateRequest $request, LanguageService $language, Order $orders, Customer $customers, Product $products)
    {
        $customer = $customers->firstOrCreate(['email' => $request->input('customer.email')], [
            'name' => $request->input('customer.fullname'),
            'email' => $request->input('customer.email'),
            'phone' => $request->input('customer.phone'),
            'country' => $language->getCurrentCountry(),
            'company_name' => $request->input('customer.company_name'),
            'city' => $request->input('customer.city'),
            'optin_newsletter' => $request->input('customer.newsletter')
        ]);

        $product = $products->find($request->input('cart.product_id'));

        $pricing = $product->getCountryPrice($language->getCurrentCountry());

        if (! $pricing)
        {
            return response()->json([
                'error' => trans(self::ERROR_PRODUCT_COUNTRY_UNAVAILABLE, ['productName' => $product->name])
            ], 422);
        }

        $order = $orders->create([
            'customer_id'        => $customer->id,
            'product_id'         => $product->id,
            'product_flavor'     => $request->input('cart.flavor'),
            'amount'             => $pricing->amount,
            'vat_amount'         => $pricing->vat_amount,
            'currency'           => $pricing->currency,
            'carehome_selection' => $request->input('carehome.custom'),
            'carehome_name'      => $request->input('carehome.name'),
            'carehome_type'      => $request->input('carehome.type'),
            'carehome_address'   => $request->input('carehome.address'),
            'carehome_city'      => $request->input('carehome.city'),
            'carehome_notes'     => $request->input('carehome.notes'),
            'custom_message'     => $request->input('gifting_options.message'),
            'is_anonymous'       => $request->input('gifting_options.anonymous'),
            'wants_certificate'  => $request->input('gifting_options.certificate'),
            'status'             => Order::STATUS_PENDING,
            'type'               => $request->input('payment_type')
        ]);

        $mollie = $order->getOrCreateMollie();

        return response()->json([
            'mollie_redirect' => $mollie->getCheckoutUrl()
        ]);
    }
}

Кроме того, если я пытаюсь временно удалить маршруты API и все еще пытаюсь получить к ним доступ, я, как ни странно, получаю 404, что означает, что Laravel может обнаружить маршрут, но использует неправильный ответ контроллера.

Как я могу это исправить?


person GiamPy    schedule 01.10.2018    source источник
comment
Не могли бы вы опубликовать часть своего кода OrdersController? Поскольку вы смогли получить 404, я предполагаю, что вы перезапустили веб-службу после изменения маршрутов. Мне пришлось запустить php artisan clear:cache или php artisan route:cache, прежде чем очистить.   -  person Daniel Gale    schedule 01.10.2018
comment
Готово @DanielGale   -  person GiamPy    schedule 01.10.2018
comment
Так же очистил всякие кеши, результат не меняется.   -  person GiamPy    schedule 01.10.2018
comment
Вы вызываете URL-адрес в ответе для Controllers @ Index `return response () -› json (['mollie_redirect' = ›$ mollie-› getCheckoutUrl ()]); «Это как-то связано с этим?   -  person Daniel Gale    schedule 01.10.2018
comment
Это находится в OrdersController, а не в Controller @ index. По сути, это просто return view('index').   -  person GiamPy    schedule 01.10.2018
comment
Итак, вы говорите OrdersController.store вернуть представление Controller @ index. Попробуйте изменить эту строку на return 'some text' или return response()->json(['test'=>'some data']); и посмотрите, что у вас получится.   -  person Daniel Gale    schedule 01.10.2018
comment
Позвольте нам продолжить это обсуждение в чате.   -  person GiamPy    schedule 01.10.2018


Ответы (4)


Я попытался настроить два заголовка, как было предложено выше, но у меня это не сработало. Вместо этого я изменил регулярное выражение маршрута для соответствия URL-адресам, которые не начинаются с api, и это сработало:

Route::view('/{any?}', 'app')
->where('any', '^(?!api).*');
person vlatkorun    schedule 17.01.2019
comment
Это не связано с моей проблемой. - person GiamPy; 18.01.2019

Подобно тому, что сказал @Marcin Nabialek, это проблема с одним из заголовков, который якобы должен был быть отправлен с запросом. Однако это был не Content-Type, а скорее Accept.

Вы должны использовать Accept: application/json, чтобы получить ответ JSON для API, по крайней мере, так он ведет себя в Laravel 5.7.6.

person GiamPy    schedule 02.10.2018

Прежде всего - когда вы удаляете маршруты api, не существует маршрута для POST метода (потому что ваш универсальный маршрут с подстановочными знаками предназначен только для запросов GET или HEAD). Вот почему вы получаете HTTP 404 - маршрут для этого запроса не найден.

Если вы добавляете маршруты api, как описано в вопросе, предоставленный ответ кажется необработанным видом ветки (возможно, макетом). Я предполагаю, что вы трижды проверили, что ваш OrdersController не может реагировать таким образом - если нет, попробуйте добавить return '{}'; в качестве первой строки вашего контроллера и посмотрите, что произойдет.

В любом случае - это может иметь какое-то отношение к типу запроса (вы устанавливаете заголовок запроса на application/x-www-form-urlencoded) - RouteServiceProvider или, возможно, api промежуточное ПО имеют какое-то отношение к этому. Попробуйте установить заголовок запроса, например, на application/json, и покопайтесь в промежуточном программном обеспечении RouteServiceProvider и api.

person Kleskowy    schedule 01.10.2018
comment
Я попытался добавить dd('test') в метод хранилища OrdersController, но он никогда не отображается. - person GiamPy; 01.10.2018

Я думаю, что здесь происходит сочетание двух вещей:

  • Вы не устанавливаете здесь Content-Type на application/json, поэтому приложение "думает", что эта обычная форма отправлена
  • Вы используете настраиваемый класс проверки OrderCreateRequest и, вероятно, проверка завершается ошибкой. Вот почему, если вы поместите dd('test'); в свой метод контроллера, он вообще не будет выполняться

В случае сбоя проверки валидатор выдает ValidationException исключение, и реализация того, что происходит в этом случае, выглядит следующим образом:

protected function convertValidationExceptionToResponse(ValidationException $e, $request)
{
    if ($e->response) {
        return $e->response;
    }

    return $request->expectsJson()
                ? $this->invalidJson($request, $e)
                : $this->invalid($request, $e);
}

Таким образом, если для запроса AJAX или Content-Type установлено значение application/json (более или менее), он вернет ответ JSON с неудачной проверкой, в противном случае будет выполнено перенаправление.

person Marcin Nabiałek    schedule 01.10.2018
comment
Я буквально просто попытался установить Content-Type на application/json, но безуспешно. Я убедился, что проверка не завершилась ошибкой, и даже если это произойдет, она должна выдать мне ошибки проверки как объект JSON вместо неправильного ответа, который не имеет ничего общего с маршрутом, который я вызываю. - person GiamPy; 02.10.2018