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

时间:2019-07-15 15:10:47

标签: php laravel cors

因此,我将Laravel 5.8用作API视图的ReactJS

我已经创建了一个“ cors”中间件,将其注册在 Kernel.php 文件中,并在iam使用的api路由上iam使用它。我使用GET请求进行了测试,并且可以正常工作,但是当我使用POST请求进行测试时,出现了cors错误:

  

从原点“ http://localhost:8000/api/posts”到“ http://localhost:3000”的获取操作已被CORS策略阻止:对预检请求的响应未通过访问控制检查:否“ Access-Control-Allow-来源的标头出现在请求的资源上。如果不透明的响应满足您的需求,请将请求的模式设置为“ 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。有人已经有这个问题了吗?

4 个答案:

答案 0 :(得分:1)

对我来说,唯一解决此问题的方法是将cors中间件类放在$middlewareKernel.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;
    }

}

希望有一天会有帮助。

答案 1 :(得分:1)

我通过使用FormData而不是JSON.stringfy解决了这个问题:

所以,我改变了:

    Bitmap bitmap = Bitmap.createBitmap(24, 24, Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap);
    canvas.drawColor(Color.MAGENTA);
    BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(), bitmap);
    getWindow().setBackgroundDrawable(bitmapDrawable);

收件人:

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

完整的代码如下:

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

答案 2 :(得分:1)

因此,当我在 api.php 中使用自己的 CORS 中间件而不使用水果蛋糕 (barryvdh/laravel-cors) 时,我遇到了同样的问题,并花了数小时进行调试并找出问题所在。

经过数小时的调试和挫折,我发现当您在组中使用中间件时,它不会立即得到应用。

laravel 如何匹配路由和“应用”中间件:

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

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

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

当您向 POST 发送 PUTDELETEPATCH/api/posts 请求时,浏览器首先发送 OPTIONS 请求,所以 laravel 会“注册”所有路由,然后使用 URL 和 HTTP 方法执行“匹配”(现在是 OPTIONS)。

但是没有具有 OPTIONS 方法的路由,resources 也没有 OPTIONS 方法,因此由于没有具有 {{1} 方法的路由} 方法,laravel 不匹配任何内容,因此它不会执行那些您最终处理 OPTIONS 方法的中间件。

<块引用>

OPTIONS 示例

api.php

Laravel 中的匹配函数:

“匹配”函数称为 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; } 记录 $route,然后发出 error_log(json_encode($route), 0); 请求,然后查看错误日志时,您可以看到成功的“匹配”并且它应用了 {{1 }} 控制器: 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 请求失败了CORS 错误 (OPTIONS)

总结和解决方案:

  • PUT 数组中的中间件被应用之后 Laravel 成功地将路由与路径和 HTTP 方法匹配,因为不同的路由可以有不同的中间件。
  • Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. 数组中的中间件(全局中间件)在 Laravel 开始注册和匹配路由之前应用

要解决此问题,您必须$routeMiddleware 数组中使用全局中间件来处理 $middleware 方法。您可以只使用处理它的 Fruitcake 一个,然后您可以在 $middleware 中使用您自己的 CORS 中间件,该中间件可以根据您的喜好设置不同的标头(例如,不同路由/组/前缀的不同 Allowed Origins .

答案 3 :(得分:0)

将您的中间件更改为此

<?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