[英]Laravel Cors Middleware not working with POST Request
所以我使用Laravel 5.8
作为API
到ReactJS
视图。
我已经创建了一个“cors”中间件,我在Kernel.php文件上注册了它,我在我正在使用的 api-routes 上使用它。 我使用 GET 请求进行了测试,它可以工作,但是当我使用 POST 请求进行测试时,我收到 cors 错误:
Access to fetch at ' http://localhost:8000/api/posts ' from origin ' http://localhost:3000 ' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access -Control-Allow-Origin' header 存在于请求的资源上。 如果不透明的响应满足您的需求,请将请求的模式设置为“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
。 有人已经有这个问题了吗?
将您的中间件更改为此
<?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');
}
}
但是一旦投入生产,您需要通过环境变量来限制允许的主机。
为我解决这个问题的唯一方法是将 cors 中间件类放在 Kernel.php 中$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;
}
}
希望有一天它会帮助某人。
我通过使用 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));
所以我遇到了同样的问题,花了几个小时调试并找出当我在 api.php 中使用我自己的 CORS 中间件而不使用水果蛋糕(barryvdh/laravel-cors)时出了什么问题。
经过数小时的调试和挫折,我发现当您在组中使用中间件时,它不会立即得到应用。
当您发送请求时,laravel 会读取 api.php 并“注册”所有路由和中间件并“记住”它们,而无需实际执行它们。 在它“注册”所有这些(读取整个api.php
文件)之后,它执行一个函数,在该函数中它输入来自 URL 的路径和请求中使用的 HTTP 方法,然后它开始查找匹配的路由URL 和 HTTP 方法,找到一个后,它执行该路由所在的那些中间件,然后执行控制器方法。
例如,当您向/api/posts
发送GET
请求时,您的代码会匹配资源方法index
,然后执行中间件cors
,因此它可以工作并从您的控制器返回数据。
当您向/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');
“匹配”函数称为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
时error_log(json_encode($route), 0);
,然后发出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错误(失败的请求Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
)
$routeMiddleware
数组中的中间件在 Laravel 成功将路由与路径和 HTTP 方法匹配后应用,因为不同的路由可以有不同的中间件。$middleware
数组(全局中间件)中的中间件在Laravel 开始注册和匹配路由之前被应用。 要解决它,您必须在$middleware
数组中使用全局中间件,该$middleware
处理OPTIONS
方法。 您可以只使用处理它的Fruitcake一个,然后您可以在api.php
中使用您自己的 CORS 中间件,该中间件可以根据您的喜好设置不同的标头(例如,不同的路由/组/前缀的不同 Allowed Origins。
只需将路线类型更改为任何
Route::any(/*your path goes here*/)->middleware('cors');
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.