Laravel 5:API路由+通配符路由会导致意外行为

时间:2018-10-01 12:07:59

标签: php laravel laravel-5 laravel-routing laravel-5.7

我正在尝试构建一个以Laravel API为后盾的React应用程序,因此实质上是使用通配符路由进行客户端路由,然后仅使用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']);

结果为:

enter image description here

但是,每当我尝试在POST api/orders发出请求时,这就是邮递员提供的信息:

enter image description here

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能够检测到该路由,但是它使用了错误的Controller响应。

我该如何解决?

4 个答案:

答案 0 :(得分:1)

类似于@Marcin Nabialek所说的,这是应该与请求一起发送的标头之一的问题。但是,它不是Content-Type,而是Accept

您必须使用Accept: application/json才能收到API的JSON响应,至少这是Laravel 5.7.6中的行为。

答案 1 :(得分:0)

首先-删除api路由时,POST方法没有没有路由(因为通配符“ catch-all”路由仅适用于GETHEAD个请求)。这就是为什么您获得HTTP 404的原因-找不到此请求的路由。

如果按照问题所述添加api路由-提供的响应似乎是原始的树枝视图(可能是布局)。我假设您三重检查了一下,您的OrdersController无法以这种方式做出响应-否则,请尝试添加return '{}';作为控制器的第一行,看看会发生什么。

无论如何-它可能与请求类型有关(您将请求标头设置为application/x-www-form-urlencoded)-RouteServiceProvider或api中间件与之有关。例如,尝试将请求标头设置为application/json,然后深入研究RouteServiceProvider和api中间件。

答案 2 :(得分:0)

我想这是两件事的结合:

  • 您没有在此处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响应,验证失败,否则将进行重定向。

答案 3 :(得分:0)

我尝试像上面建议的那样设置两个标头,但对我来说不起作用。相反,我修改了路由正则表达式以匹配以api开头的网址,并且有效:

Route::view('/{any?}', 'app')
->where('any', '^(?!api).*');