Laravel/React:barryvdh/laravel-cors 处理预检 HTTP OPTIONS 请求

Posted

技术标签:

【中文标题】Laravel/React:barryvdh/laravel-cors 处理预检 HTTP OPTIONS 请求【英文标题】:Laravel/React: barryvdh/laravel-cors handling preflight HTTP OPTIONS request 【发布时间】:2019-08-29 02:21:41 【问题描述】:

刚刚开始使用barryvdh/laravel-cors。我有一个在端口 8000 上运行的服务器端应用程序,它通过 Laravel Passport 对用户进行身份验证,而我的 react.js 客户端应用程序在端口 3000 上运行。现在我正在尝试让“登录”操作正常工作。

当我提供正确的凭据时,客户端会发回一个令牌,状态为 200。当凭据无效时,会返回 401。到目前为止这么好。但是,如果我在表单中只提供电子邮件而没有密码,我会收到以下与 CORS 相关的错误:

抛出错误时,会发生以下网络调用:

我在客户端的登录:

login = (email, password) => 
    console.log(email);
    fetch("http://localhost:8000/api/auth/login", 
        method: 'POST',
        body: JSON.stringify(
            'email': email,
            'password': password
        ),
        headers: 
            'Content-Type': 'application/json'
        
    )
    .then(res => res.json())
    .then(response => 
        console.log(response['access_token']); //with correct credentials this is logged out and set in local storage
        localStorage.setItem('access_token', response['access_token']);
    )
    .catch(error => console.error('Error: ', error));

服务器端:

public function login(Request $request)

    $request->validate([
        'email' => 'required|string|email',
        'password' => 'required|string',
        'remember_me' => 'boolean'
    ]);

    $credentials = request(['email', 'password']);

    if(!Auth::attempt($credentials)) 
        return response()->json([
            'message' => 'Unauthorized'
        ], 401);
    

    $user = $request->user();
    $tokenResult = $user->createToken('Personal Access Token');
    $token = $tokenResult->token;

    if ($request->remember_me) 
        $token->expires_at = Carbon::now()->addWeeks(1);
    

    $token->save();
    return response()->json([
        'access_token' => $tokenResult->accessToken,
        'token_type' => 'Bearer',
        'expires_at' => Carbon::parse(
            $tokenResult->token->expires_at
        )->toDateTimeString()
    ]);

cors.php(从 barryvdh/laravel-cors 包发布):

return [
    'supportsCredentials' => false,
    'allowedOrigins' => ['*'],
    'allowedOriginsPatterns' => [],
    'allowedHeaders' => ['*'],
    'allowedMethods' => ['*'],
    'exposedHeaders' => [],
    'maxAge' => 0,
];

api.php:

Route::group(['prefix' => 'auth',], function () 
    Route::post('login', 'AuthController@login');
    Route::post('register', 'AuthController@register');
    Route::get('register/activate/token', 'AuthController@registerActivate');
    Route::group(['middleware' => 'auth:api'], function() 
        Route::get('logout', 'AuthController@logout');
        Route::get('user', 'AuthController@user');
    );
);

Kernel.php:

protected $middlewareGroups = [
    'web' => [
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        // \Illuminate\Session\Middleware\AuthenticateSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \App\Http\Middleware\VerifyCsrfToken::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],

    'api' => [
        \Barryvdh\Cors\HandleCors::class,
        'throttle:60,1',
        'bindings',
    ],
];

protected $middleware = [
    \Barryvdh\Cors\HandleCors::class,
    \App\Http\Middleware\CheckForMaintenanceMode::class,
    \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
    \App\Http\Middleware\TrimStrings::class,
    \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
    \App\Http\Middleware\TrustProxies::class
];

通过邮递员发出相同的 POST 请求(向/api/auth/login 发送一个不包含密码的请求)我收到了预期的错误消息:


    "message": "The given data was invalid.",
    "errors": 
        "password": [
            "The password field is required."
        ]
    

但是,通过 react 客户端应用程序进行此操作,会引发屏幕截图中的错误。我猜这与浏览器作为 CORS 工作方式的一部分发送的预检 HTTP OPTIONS 请求有关。

我该如何纠正这个问题?我希望为状态返回 401,并让反应客户端在密码为空的情况下获得 Postman 收到的相同 JSON 消息。根据该 POST 请求的有效负载,出现与 CORS 相关的错误似乎很奇怪。这是否意味着服务器和/或客户端没有正确设置 CORS?

【问题讨论】:

laravel 和 react 应用都需要正确发送 CORS 头。如果您可以避免 CORS 重定向,那么处理起来会容易得多 我以为只有服务器端需要设置 CORS 相关的标头。客户端需要设置哪些标头?我认为我无法避免 CORS 重定向 在这一点上看起来很棒,您能否在您的Kernel.php 中添加config/app.php 内容和中间件部分? 我看到\Barryvdh\Cors\HandleCors::class,$middleware$middlewareGroups 中都声明了,您可以尝试从$middlewareGroups 部分中删除它吗,另一件事,我注意到很多人有这个问题,它似乎与包的版本有关,你使用的是什么版本?看看这个问题:link1, link2 不客气,我会添加这个作为答案。 【参考方案1】:

好像是你的后端发生错误时抛出的错误。

正如docs中提到的:

发生错误时,中间件未完全运行。所以当 发生这种情况,您不会看到实际结果,但会得到一个 CORS 而是错误。

这个issue 帮助找出了错误的根源

【讨论】:

【参考方案2】:

首先是班级成员:

protected $middleware

将为apiweb 应用中间件

发件人 laravel.com/docs/5.8/middleware#registering-middleware

如果您希望在对应用程序的每个 HTTP 请求期间运行中间件,请在 app/Http/Kernel.php 类的 $middleware 属性中列出中间件类。

另请参阅: barryvdh/laravel-cors#global-usage

因此,请尝试通过删除以下内容来注册您的中间件: \Barryvdh\Cors\HandleCors::class 属于您的'api' 中间件组

出于逻辑目的,重构您的protected $middleware

protected $middleware = [
    \App\Http\Middleware\CheckForMaintenanceMode::class,
    \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
    \App\Http\Middleware\TrimStrings::class,
    \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
    \App\Http\Middleware\TrustProxies::class,
    \Barryvdh\Cors\HandleCors::class,
];

【讨论】:

以上是关于Laravel/React:barryvdh/laravel-cors 处理预检 HTTP OPTIONS 请求的主要内容,如果未能解决你的问题,请参考以下文章

Laravel 5.4 laravel-elixir-webpack-react

如何使用 Laravel + React 检查上传文件的类型?

Laravel - React,强制 Axios 进行拒绝/捕获

在 Laravel 5.7 React 应用程序中,收到错误:“classProperties”当前未启用

React报错:Laravel React-Router browserHistory 刷新子页面报错404

在 Laravel + React SPA 应用程序中应用 TDD