简体   繁体   English

Laravel Cors 中间件不适用于 POST 请求

[英]Laravel Cors Middleware not working with POST Request

So I am using Laravel 5.8 as an API to a ReactJS view.所以我使用Laravel 5.8作为APIReactJS视图。

I already created a 'cors' middleware, i registered it on Kernel.php file, and I am using it on the api-routes that I am using.我已经创建了一个“cors”中间件,我在Kernel.php文件上注册了它,我在我正在使用的 api-routes 上使用它。 I tested using a GET Request and it worked, but when I test with a POST Request, I get the cors error:我使用 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 is present on the requested resource. 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 存在于请求的资源上。 If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.如果不透明的响应满足您的需求,请将请求的模式设置为“no-cors”以获取禁用 CORS 的资源。

So i have on my api.php ("/routes/api.php"):所以我在我的api.php (“/routes/api.php”)上有:

Route::get('/posts', 'PostController@index')->middleware('cors');
Route::post('/posts', 'PostController@store')->middleware('cors');

My cors.php middleware:我的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');
  }
}

On my Kernel.php ("/app/Http/Kernel.php") I updated the "$routeMiddleware" array with my 'cors' middleware在我的Kernel.php ("/app/Http/Kernel.php") 上,我用我'cors'中间件更新了“$routeMiddleware”数组

'cors' => \App\Http\Middleware\Cors::class, 

Now in my React project, my api.js (where I made the code to make the requests):现在在我的 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));
}

I don't understand why everything is working fine when i try the Get request , but when i try the Post Request , i get the CORS error .我不明白为什么在我尝试Get request时一切正常,但是当我尝试Post Request时,我收到CORS error Someone already had this problem?有人已经有这个问题了吗?

change your middleware to this将您的中间件更改为此

<?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');
  }
}

but once in production, you need to limit the allowed hosts by an environment variable.但是一旦投入生产,您需要通过环境变量来限制允许的主机。

You can also just use barryvdh/laravel-cors Link here您也可以在此处使用barryvdh/laravel-cors链接

The only thing that solved this issue for me was to put the cors middleware class in the top of $middleware array in Kernel.php为我解决这个问题的唯一方法是将 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,

    ];

here is the cors middleware i am using这是我正在使用的 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;
    }

}

hope it will help someone someday.希望有一天它会帮助某人。

I solved this issue by using FormData instead of JSON.stringfy:我通过使用 FormData 而不是 JSON.stringfy 解决了这个问题:

So, I changed:所以,我改变了:

let data = JSON.stringify({firstname:'John', familyname: 'Doe'});

to:到:

let data = new FormData();
data.append('firstname','John');
data.append('lastname','Doe');

and the complete code is something like this:完整的代码是这样的:

fetch(YOUR_API_URL, {
    method: 'POST',
    body: data
    }).then(
    res =>res.json()
        .then(res => console.log(res))
    ).catch(err => console.log(err));

So I was having the same problem and spent hours debugging and figuring out what was wrong when I was using my own CORS middlewares in api.php without using the fruitcake one (barryvdh/laravel-cors).所以我遇到了同样的问题,花了几个小时调试并找出当我在 api.php 中使用我自己的 CORS 中间件而不使用水果蛋糕(barryvdh/laravel-cors)时出了什么问题。

After hours of debugging and frustration I figured out that when you use a middleware in a group, then it doesn't get applied right away.经过数小时的调试和挫折,我发现当您在组中使用中间件时,它不会立即得到应用。

How laravel matches routes and "applies" middlewares: laravel 如何匹配路由和“应用”中间件:

When you send a request, laravel reads the api.php and just "registers" all the routes and middlewares and "remembers" them without actually executing them.当您发送请求时,laravel 会读取 api.php 并“注册”所有路由和中间件并“记住”它们,而无需实际执行它们。 After it "registers" all of them (reads the whole api.php file), it executes a function where it inputs the path from the URL and the HTTP method that was used in the request and then it begins to find the route that matches the URL and HTTP method and after it finds one, it executes those middlewares that this route is located in and then it executes the controller methods.在它“注册”所有这些(读取整个api.php文件)之后,它执行一个函数,在该函数中它输入来自 URL 的路径和请求中使用的 HTTP 方法,然后它开始查找匹配的路由URL 和 HTTP 方法,找到一个后,它执行该路由所在的那些中间件,然后执行控制器方法。

So for example with your code when you send a GET request to /api/posts , it matches the resource method index and then executes the middleware cors and therefore it works and returns data from your controller.例如,当您向/api/posts发送GET请求时,您的代码会匹配资源方法index ,然后执行中间件cors ,因此它可以工作并从您的控制器返回数据。

Why POST, PUT, DELETE and PATCH don't work with this approach:为什么 POST、PUT、DELETE 和 PATCH 不适用于这种方法:

When you send a POST , PUT , DELETE or PATCH request to /api/posts , the browser sends an OPTIONS request first, so laravel "registers" all the routes and then it executes the "matching" using the URL and the HTTP method (it is OPTIONS right now).当您向/api/posts发送POSTPUTDELETEPATCH请求时,浏览器首先发送一个OPTIONS请求,因此 laravel 会“注册”所有路由,然后使用 URL 和 HTTP 方法执行“匹配”(现在是OPTIONS )。

But there is no route that has a method of OPTIONS and resources don't have an OPTIONS method either, so since there is no route that has an OPTIONS method, laravel doesn't match anything and therefore it does not execute those middlewares where you eventually handle OPTIONS methods.但是没有具有OPTIONS方法的路由, resources也没有具有OPTIONS方法的路由,因此由于没有具有OPTIONS方法的路由,laravel 不匹配任何内容,因此它不会执行您所在的那些中间件最终处理OPTIONS方法。

api.php example api.php示例

Route::post('/posts', 'PostController@store')->middleware('cors');

The matching function in Laravel: Laravel 中的匹配函数:

The "matching" function is called findRoute and it is located in vendor/laravel/framework/src/Illuminate/Routing/Router.php . “匹配”函数称为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; }

When you log $route with error_log(json_encode($route), 0);当您使用error_log(json_encode($route), 0);记录$routeerror_log(json_encode($route), 0); , then make a GET request and then look in error logs, you can see the succesful "match" and that it applied the cors controller: {"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":{}} ,然后发出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":{}}

But when you send a POST request, this happens: {"uri":"api\\/posts","methods":["OPTIONS"],"action":{"uses":{}},"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":{}}

There you can see that actually OPTIONS method was sent (because browser sends an OPTIONS method first) and nothing got matched and no middleware was applied and therefore the PUT request failed with a CORS error ( Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. )在那里,你可以看到,实际上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.

The summary and solution:总结及解决方法:

  • Middlewares in $routeMiddleware array get applied after Laravel succefully matches a route with the path and the HTTP method because different routes can have different middlwares. $routeMiddleware数组中的中间件在 Laravel 成功将路由与路径和 HTTP 方法匹配应用因为不同的路由可以有不同的中间件。
  • Middlewares in $middleware array (global middlewares) get applied before Laravel begins registering and matching routes. $middleware数组(全局中间件)中的中间件Laravel 开始注册和匹配路由之前被应用。

To solve it, you have to use a global middleware in $middleware array, that handles the OPTIONS method.要解决它,您必须$middleware数组中使用全局中间件,该$middleware处理OPTIONS方法。 You can just use the Fruitcake one that handles it and then you can use your own CORS middlewares in api.php that can set different headers to your liking (for example different Allowed Origins for different routes/groups/prefixes.您可以只使用处理它的Fruitcake一个,然后您可以在api.php中使用您自己的 CORS 中间件,该中间件可以根据您的喜好设置不同的标头(例如,不同的路由/组/前缀的不同 Allowed Origins。

Just change the route type to any只需将路线类型更改为任何

Route::any(/*your path goes here*/)->middleware('cors');

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM