Laravel 7 中对 localhost:3000 的 CORS 支持

Posted

技术标签:

【中文标题】Laravel 7 中对 localhost:3000 的 CORS 支持【英文标题】:CORS support for localhost:3000 in Laravel 7 【发布时间】:2020-06-19 22:25:22 【问题描述】:

在此处发布之前,我确保在网上查找可能的问题解决方案。

我使用 Nuxt s-s-r 作为前端,使用 Laravel 7 作为后端 API,它现在具有本机 CORS 实现。我的前端应用程序在http://localhost:3030 运行,api 在http://gaminghub.test 运行。

这是我在最新版 laravel 自带的cors.php 里的:

<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Cross-Origin Resource Sharing (CORS) Configuration
    |--------------------------------------------------------------------------
    |
    | Here you may configure your settings for cross-origin resource sharing
    | or "CORS". This determines what cross-origin operations may execute
    | in web browsers. You are free to adjust these settings as needed.
    |
    | To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
    |
    */

    'paths' => ['api/*'],

    'allowed_methods' => ['*'],

    'allowed_origins' => ['*'],

    'allowed_origins_patterns' => [],

    'allowed_headers' => ['*'],

    'exposed_headers' => false,

    'max_age' => false,

    'supports_credentials' => true,

];

使用邮递员,这是向http://gaminghub.test/api/products?category=play-card发送get请求时返回的内容:


    "data": [
        
            "id": 1,
            "name": "Xiaomi",
            "price": 2000000,
            "description": "Best value for money",
            "slug": "xiaomi"
        
    ],
    "links": 
        "first": "http://gaminghub.test/api/products?page=1",
        "last": "http://gaminghub.test/api/products?page=2",
        "prev": null,
        "next": "http://gaminghub.test/api/products?page=2"
    ,
    "meta": 
        "current_page": 1,
        "from": 1,
        "last_page": 2,
        "path": "http://gaminghub.test/api/products",
        "per_page": 1,
        "to": 1,
        "total": 2
    

nuxt.config.js 包括:

  /*
   ** Axios module configuration
   ** See https://axios.nuxtjs.org/options
   */
  axios: 
    baseURL: "http://gaminghub.test/api",
    credentials: true
  ,

product.vue 包含以下内容以检索数据:

<script>
export default 
    data() 
    return 
      product: null
    
  ,

  async asyncData(params, app) 
    let res = await app.$axios.$get(`products/?category=$params.slug`)

    return 
      product: res.data
    
  ,

</script>

所以,基本上,当我点击每个类别时,他们会向api/products?category=clicked-category-slug 发送一个获取请求,这会导致控制台出现以下错误:

Access to XMLHttpRequest at 'http://gaminghub.test/api/products/?category=play-card' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.

网络请求头包含以下内容:

Request Headers Provisional headers are shown Accept: application/json, text/plain, */* Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC9nYW1pbmdodWIudGVzdFwvYXBpXC9hdXRoXC9sb2dpbiIsImlhdCI6MTU4MzU3MjQ4MiwiZXhwIjoxNTgzNTc2MDgyLCJuYmYiOjE1ODM1NzI0ODIsImp0aSI6ImdYY1Q3WGRSVnFXdUpocFIiLCJzdWIiOjMsInBydiI6Ijg3ZTBhZjFlZjlmZDE1ODEyZmRlYzk3MTUzYTE0ZTBiMDQ3NTQ2YWEifQ.t4JLsLLE6WIFZp67hbafZl8YTLlzN2WyQVw11mETMNQ Referer: http://localhost:3000/categories/play-card User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (Khtml, like Gecko) Chrome/80.0.3987.132 Safari/537.36

当我重新加载浏览器时,错误消失并返回预期的数据。 Nuxt 正在universal s-s-r 模式下运行。

我需要一些指导来了解可能导致此问题的原因。我一定是错过了什么

【问题讨论】:

我猜products/?category=... 中的额外斜线会导致自动重定向到products?category=...。检查响应标头以确认,或尝试删除斜杠。 就是这样。我怎么可能没有注意到这一点。非常感谢! 【参考方案1】:

“预检请求不允许重定向”错误。通常在服务器向客户端发送“位置”响应标头时发生。浏览器不喜欢这样,因此会引发错误。因此,您在 url /api/products?category=play-card 上的 laravel 控制器可能会发送 Location 标头,我不确定。 例如,您可以做些什么来调试此问题,即在您请求此 url 时使用 Postman 检查发送给客户端的标头。

希望这会对你有所帮助。

PS:我是在这里回答问题的初学者,所以可能不是正确的答案。

【讨论】:

【参考方案2】:

这就是我使用 react 16 和 laravel 7 的方式

在我的 Controller 中,我只是使用了welcomeController 作为存储在 app\http 中的示例

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;

class WelcomeController extends Controller

    public function show() 
        return view('welcome');
    

    public function post(Request $request) 
        // extra data that may come from a database 
        // or a token or whatever your external component needs
        $post = [
            'title' => 'my title',
            'content' => 'Lorem, ipsum dolor sit amet consectetur adipisicing elit. Hic velit, harum neque adipisci praesentium quo voluptate laudantium ipsam optio provident accusamus modi nobis, facere atque iure perferendis corporis? Quam, distinctio.'
        ];
        $response = array_merge($request->all(), $post);
        return json_encode($response);
    

在 /app/http/Middleware 中

添加文件 Cors.php。添加以下代码

<?php
namespace App\Http\Middleware;
use Closure;
class Cors

  public function handle($request, Closure $next)
  
    return $next($request)
      ->header('Access-Control-Allow-Origin', 'http://localhost:3000')
      ->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
      ->header('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, X-Token-Auth, Authorization');
  

接下来在 ./routes 中构建您的 api,如下所示

// the order or the middleware matter so make sure that
// cors middleware is first before the api middleware 
Route::middleware('cors:api')->post('/post', 'WelcomeController@post');

在 app/config/cors.php 中,保持这个文件不变

[
    'paths' => ['api/*'],

    'allowed_methods' => ['*'],

    'allowed_origins' => ['*'],

    'allowed_origins_patterns' => [],

    'allowed_headers' => ['*'],

    'exposed_headers' => [],

    'max_age' => 0,

    'supports_credentials' => false,

];

在受保护的 $routeMiddleware 数组中的 app/Http/Kernel.php 中附加一个新的键/值对,参见下面的示例:

 protected $routeMiddleware = [
    'auth' => \App\Http\Middleware\Authenticate::class,
    'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
    'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
    'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
    'can' => \Illuminate\Auth\Middleware\Authorize::class,
    'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
    'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
    'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
    'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
    'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
    'cors' => \App\Http\Middleware\Cors::class, // add route middleware
];

然后在您的 UI 中,您可以测试传入的数据是否通过,我使用 react 但我使用原生 javascript Fetch api 来发布帖子。下面是调用并将信息呈现给 DOM 的组件

import React,  Component  from 'react';

class ConnectToApi extends Component 
  
  state = 
    data: []
  

  componentDidMount() 
    this.handleGetData();
  

  handleGetData = async () => 
    fetch('http://laravel-api.local/api/post', 
      method: 'post',
      mode: 'cors',
      headers: new Headers(
        'Accept': 'application/json',
        'Content-Type': 'application/json',
      ),
      body: JSON.stringify(
        name: 'John',
        surname: 'Doe',
      )
    )
    .then(response => response.json())
    .then(data => 
      const state = Object.assign(, this.state)
      state.data.push(data);
      this.setState(state);      
    )
    .catch(error => console.log(error));
  

  render() 
    console.log(this.state.data);
    
    return <div>
      <header>
      <h1>
        data
      </h1>
    </header>
    <p>Modified data</p>
    this.state.data.length === 0 && <p>loading data...</p>
    
      this.state.data.length > 0  && this.state.data.map((value, index) => 
        return (
          <div key=index>
            <p>name: value.name</p>
            <p>surname: value.surname</p>
            <p>title: value.title</p>
            <p>Content <br/><br/> value.content</p>
          </div>
        )
      )
    
    </div>
  

export default ConnectToApi;

然后在 app.js 中只有

import React from 'react';
import logo from './logo.svg';
import './App.css';
import ConnectToApi from './connectToApi'

function App() 
  return (
    <div className="App">
      <ConnectToApi />
    </div>
  );


export default App;

【讨论】:

以上是关于Laravel 7 中对 localhost:3000 的 CORS 支持的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Laravel 中对数组的值进行排序

在 Laravel 中对分页变量进行排序

在 laravel 中对多列使用 distinct

只有买家可以在 Livewire:Laravel 中对产品进行评分

如何在 Laravel 中对所有行使用多态关系?

如何在 Laravel 4 中对多列使用排序依据?