繁体   English   中英

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

[英]Laravel Cors Middleware not working with POST Request

所以我使用Laravel 5.8作为APIReactJS视图。

我已经创建了一个“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');
  }
}

但是一旦投入生产,您需要通过环境变量来限制允许的主机。

您也可以在此处使用barryvdh/laravel-cors链接

为我解决这个问题的唯一方法是将 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 如何匹配路由和“应用”中间件:

当您发送请求时,laravel 会读取 api.php 并“注册”所有路由和中间件并“记住”它们,而无需实际执行它们。 在它“注册”所有这些(读取整个api.php文件)之后,它执行一个函数,在该函数中它输入来自 URL 的路径和请求中使用的 HTTP 方法,然后它开始查找匹配的路由URL 和 HTTP 方法,找到一个后,它执行该路由所在的那些中间件,然后执行控制器方法。

例如,当您向/api/posts发送GET请求时,您的代码会匹配资源方法index ,然后执行中间件cors ,因此它可以工作并从您的控制器返回数据。

为什么 POST、PUT、DELETE 和 PATCH 不适用于这种方法:

当您向/api/posts发送POSTPUTDELETEPATCH请求时,浏览器首先发送一个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);记录$routeerror_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.

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