Laravel Cors 中间件不适用于 POST 请求
Posted
技术标签:
【中文标题】Laravel Cors 中间件不适用于 POST 请求【英文标题】:Laravel Cors Middleware not working with POST Request 【发布时间】:2019-11-24 07:48:49 【问题描述】:所以我使用Laravel 5.8
作为API
到ReactJS
视图。
我已经创建了一个 'cors' 中间件,我在 Kernel.php 文件中注册了它,并且我在我正在使用的 api-routes 上使用它。我使用 GET 请求进行了测试,它可以工作,但是当我使用 POST 请求进行测试时,我得到了 cors 错误:
从源“http://localhost:3000”获取“http://localhost:8000/api/posts”的访问权限已被 CORS 策略阻止:对预检请求的响应未通过访问控制检查:没有“Access-Control-Allow-Origin”标头出现在请求的资源上。如果不透明的响应满足您的需求,请将请求的模式设置为“no-cors”以获取禁用 CORS 的资源。
所以我有我的 api.php ("/routes/api.php"):
Route::get('/posts', 'PostController@index')->middleware('cors');
Route::post('/posts', 'PostController@store')->middleware('cors');
我的 cors.php 中间件:
<?php
namespace App\Http\Middleware;
use Closure;
class Cors
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
return $next($request)
->header('Access-Control-Allow-Origin', '*')
->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
->header('Access-Control-Allow-Headers', 'Content-Type, Accept, Authorization, X-Requested-With, Application');
在我的 Kernel.php ("/app/Http/Kernel.php") 上,我使用 'cors'
中间件更新了 "$routeMiddleware" 数组
'cors' => \App\Http\Middleware\Cors::class,
现在在我的 React 项目中,我的 api.js(我在其中编写了发出请求的代码):
// get Posts
export const getPosts = () =>
return fetch('http://localhost:8000/api/posts')
.then(res => res.json())
.then(json => console.log(json))
.catch(err => console.log(err));
// create new post
export const createPost = (post) =>
return fetch('http://localhost:8000/api/posts',
method: 'post',
headers:
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json'
,
body: JSON.stringify(post)
)
.then(res => res.json())
.then(res => console.log(res));
我不明白为什么当我尝试Get request
时一切正常,但是当我尝试Post Request
时,我得到了CORS error
。有人遇到过这个问题吗?
【问题讨论】:
Access-Control-Allow-Origin: *
不被浏览器接受,您需要指定允许的域。没有更多的小丑*
【参考方案1】:
把你的中间件改成这个
<?php
namespace App\Http\Middleware;
use Closure;
class Cors
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
$domain = parse_url($_SERVER['HTTP_REFERER']);
$host = '*';
if (isset($domain['host']))
$host = $domain['host'];
return $next($request)
->header('Access-Control-Allow-Origin', $host)
->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
->header('Access-Control-Allow-Headers', 'Content-Type, Accept, Authorization, X-Requested-With, Application');
但一旦投入生产,您需要通过环境变量来限制允许的主机。
您也可以只使用barryvdh/laravel-cors
Link here
【讨论】:
感谢您的回复。我尝试了您的解决方案,但没有奏效。但是我尝试了 'barryvdh/laravel-cors' 解决方案,问题是,当我尝试 GET 请求时它正在工作(作为我的第一个 'cors' 中间件),但是当我尝试 POST 请求时,我得到了同样的错误: “CORS 策略已阻止从源 'localhost:3000' 获取 'localhost:8000/api/posts' 的访问权限:所请求的资源上不存在 'Access-Control-Allow-Origin' 标头。如果不透明的响应满足您的需求,将请求的模式设置为 'no-cors'..." 这真的很奇怪。 我发现了一些对我有帮助的东西:github.com/barryvdh/laravel-cors/issues/360 我改变了我从控制器返回的内容,奇怪的是,它现在正在发布请求。 (使用 'barryvdh/laravel-cors' 解决方案)! @JulioW。很高兴引导您找到适合您的解决方案。你的代码中有一些echo
吗?【参考方案2】:
为我解决这个问题的唯一方法是将 cors 中间件类放在 $middleware
数组的顶部 Kernel.php
protected $middleware = [
\App\Http\Middleware\Cors::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,
];
这是我正在使用的 cors 中间件
<?php
namespace App\Http\Middleware;
use Closure;
class Cors
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
$response = $next($request);
$response->header("Access-Control-Allow-Origin","*");
$response->header("Access-Control-Allow-Credentials","true");
$response->header("Access-Control-Max-Age","600"); // cache for 10 minutes
$response->header("Access-Control-Allow-Methods","POST, GET, OPTIONS, DELETE, PUT"); //Make sure you remove those you do not want to support
$response->header("Access-Control-Allow-Headers", "Content-Type, Accept, Authorization, X-Requested-With, Application");
return $response;
希望有一天它会对某人有所帮助。
【讨论】:
【参考方案3】:因此,当我在 api.php 中使用自己的 CORS 中间件而不使用水果蛋糕 (barryvdh/laravel-cors) 时,我遇到了同样的问题并花了数小时调试并找出问题所在。
经过数小时的调试和挫折后,我发现当您在组中使用中间件时,它不会立即得到应用。
laravel 如何匹配路由并“应用”中间件:
当您发送请求时,laravel 会读取 api.php 并“注册”所有路由和中间件并“记住”它们而不实际执行它们。在它“注册”所有这些之后(读取整个 api.php
文件),它执行一个函数,在该函数中输入来自 URL 的路径和请求中使用的 HTTP 方法,然后开始查找匹配的路由URL 和 HTTP 方法,找到一个后,它会执行该路由所在的那些中间件,然后执行控制器方法。
例如,当您向/api/posts
发送GET
请求时,您的代码会匹配资源方法index
,然后执行中间件cors
,因此它可以工作并从您的控制器返回数据。
为什么 POST、PUT、DELETE 和 PATCH 不适用于这种方法:
当你向/api/posts
发送POST
、PUT
、DELETE
或PATCH
请求时,浏览器会先发送OPTIONS
请求,所以laravel“注册”所有路由然后执行使用 URL 和 HTTP 方法的“匹配”(现在是 OPTIONS
)。
但是没有路由有OPTIONS
和resources
也没有OPTIONS
方法,所以由于没有路由有OPTIONS
方法,所以laravel 不匹配任何东西,因此它不会执行您最终处理 OPTIONS
方法的那些中间件。
api.php
示例Route::post('/posts', 'PostController@store')->middleware('cors');
Laravel 中的匹配函数:
“匹配”函数名为findRoute
,位于vendor/laravel/framework/src/Illuminate/Routing/Router.php
。
/** * Find the route matching a given request. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Routing\Route */ protected function findRoute($request) $this->current = $route = $this->routes->match($request); $this->container->instance(Route::class, $route); return $route;
当您使用error_log(json_encode($route), 0);
记录$route
,然后发出GET
请求,然后查看错误日志,您可以看到成功的“匹配”并且它应用了cors
控制器:
"uri":"api\/posts","methods":["GET","HEAD"],"action":"middleware":["cors"],"uses":"App\\Http\\Controllers\\PostController@index","controller":"App\\Http\\Controllers\\PostController@index","namespace":null,"prefix":"api","where":[],"isFallback":false,"controller":null,"defaults":[],"wheres":[],"parameters":[],"parameterNames":[],"computedMiddleware":null,"compiled":
但是当您发送POST
请求时,会发生这种情况:
"uri":"api\/posts","methods":["OPTIONS"],"action":"uses":,"isFallback":false,"controller":null,"defaults":[],"wheres":[],"parameters":[],"parameterNames":[],"computedMiddleware":null,"compiled":
您可以看到实际上发送了OPTIONS
方法(因为浏览器首先发送OPTIONS
方法)并且没有匹配到任何中间件,因此PUT
请求失败并出现CORS 错误(@987654354 @)
总结及解决办法:
$routeMiddleware
数组中的中间件被应用在之后 Laravel 成功地将路由与路径和 HTTP 方法匹配,因为不同的路由可以有不同的中间件。
$middleware
数组中的中间件(全局中间件)在之前应用 Laravel 开始注册和匹配路由。
要解决这个问题,您必须在$middleware
数组中使用一个全局中间件,该中间件处理OPTIONS
方法。您可以只使用处理它的Fruitcake,然后您可以在api.php
中使用您自己的 CORS 中间件,可以根据自己的喜好设置不同的标头(例如,不同路由/组/前缀的不同允许来源。
【讨论】:
【参考方案4】:我通过使用 FormData 而不是 JSON.stringfy 解决了这个问题:
所以,我改变了:
let data = JSON.stringify(firstname:'John', familyname: 'Doe');
到:
let data = new FormData();
data.append('firstname','John');
data.append('lastname','Doe');
完整的代码是这样的:
fetch(YOUR_API_URL,
method: 'POST',
body: data
).then(
res =>res.json()
.then(res => console.log(res))
).catch(err => console.log(err));
【讨论】:
以上是关于Laravel Cors 中间件不适用于 POST 请求的主要内容,如果未能解决你的问题,请参考以下文章
barryvdh/laravel-cors 不适用于我的路线
为啥 Laravel 中间件适用于单个路由而不适用于一组路由