尝试使用获取和传递模式:无 cors

我可以通过Postman点击这个端点,然后它返回http://catfacts-api.appspot.com/api/facts?number=99JSON

此外,我正在使用create-react-app,并希望避免设置任何服务器配置。

在我的客户端代码中,我试图用来做同样的事情,但我得到错误:fetch

请求的资源上不存在“访问控制-允许-源”标头。因此,不允许访问源“http://localhost:3000”。如果不透明响应满足你的需求,请将请求的模式设置为“no-cors”,以便在禁用 CORS 的情况下获取资源。

因此,我正在尝试将一个对象传递给我的 Fetch,这将禁用 CORS,如下所示:

fetch('http://catfacts-api.appspot.com/api/facts?number=99', { mode: 'no-cors'})
  .then(blob => blob.json())
  .then(data => {
    console.table(data);
    return data;
  })
  .catch(e => {
    console.log(e);
    return e;
  });

有趣的是,我得到的错误实际上是这个函数的语法错误。我不确定我的实际值是否损坏,因为当我删除{mode:'no-cors' }对象,并为其提供不同的URL时,它工作正常。fetch

我也尝试过传入对象,但这从上面返回原始错误。{ mode: 'opaque'}

我相信我需要做的就是禁用CORS。我错过了什么?


答案 1

mode: 'no-cors'不会神奇地让事情顺利进行。事实上,它使事情变得更糟,因为它的一个效果是告诉浏览器,“阻止我的前端JavaScript代码在任何情况下都看不到响应正文和标头的内容。当然,你永远都不想这样。

来自前端 JavaScript 的跨源请求所发生的情况是,默认情况下,浏览器会阻止前端代码跨源访问资源。如果在响应中,则浏览器会放宽该阻止并允许您的代码访问响应。Access-Control-Allow-Origin

但是,如果站点在其响应中不发送任何内容,则您的前端代码无法直接访问来自该站点的响应。特别是,您无法通过指定来修复它(实际上,这将确保您的前端代码无法访问响应内容)。Access-Control-Allow-Originmode: 'no-cors'

但是,有一件事是可行的:如果您通过CORS代理发送请求

您还可以在短短2-3分钟内轻松地将自己的代理部署到Heroku,只需5个命令:

git clone https://github.com/Rob--W/cors-anywhere.git
cd cors-anywhere/
npm install
heroku create
git push heroku master

运行这些命令后,您最终将拥有自己的 CORS Anywhere 服务器,例如,运行在 。https://cryptic-headland-94862.herokuapp.com/

在请求 URL 前面加上代理 URL 的前缀;例如:

https://cryptic-headland-94862.herokuapp.com/https://example.com

将代理 URL 添加为前缀会导致请求通过代理发出,这:

  1. 将请求转发到 。https://example.com
  2. 接收来自 的响应。https://example.com
  3. 将标头添加到响应中。Access-Control-Allow-Origin
  4. 将该响应(添加了该标头)传递回请求的前端代码。

然后,浏览器允许前端代码访问响应,因为带有响应标头的响应是浏览器看到的。Access-Control-Allow-Origin

即使请求是触发浏览器执行 CORS 预检请求的请求,这也有效,因为在这种情况下,代理还会发回使预检成功所需的 和 标头。OPTIONSAccess-Control-Allow-HeadersAccess-Control-Allow-Methods


我可以通过Postman点击这个端点http://catfacts-api.appspot.com/api/facts?number=99

https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS 解释了为什么即使您可以使用Postman访问响应,浏览器也不会允许您从Web应用程序中运行的前端JavaScript代码访问跨源响应,除非响应包含响应标头。Access-Control-Allow-Origin

http://catfacts-api.appspot.com/api/facts?number=99 没有响应标头,因此前端代码无法访问跨源响应。Access-Control-Allow-Origin

您的浏览器可以很好地获得响应,您可以在Postman甚至浏览器开发工具中看到它 - 但这并不意味着浏览器会将其公开给您的代码。他们不会,因为它没有响应标头。因此,您必须改用代理来获取它。Access-Control-Allow-Origin

代理向该站点发出请求,获取响应,添加响应标头和所需的任何其他 CORS 标头,然后将其传递回请求代码。添加标头的响应是浏览器看到的,因此浏览器允许前端代码实际访问响应。Access-Control-Allow-OriginAccess-Control-Allow-Origin


所以我正在尝试传入一个对象,到我的 Fetch,这将禁用 CORS

你不想那样做。需要明确的是,当您说要“禁用 CORS”时,似乎您实际上意味着要禁用同源策略。CORS本身实际上是一种做到这一点的方法 - CORS是一种放松同源策略的方法,而不是限制它的方法。

但无论如何,在本地环境中,您可以确实提供浏览器运行时标志以禁用安全性并不安全地运行,或者您可以在本地安装浏览器扩展以绕过同源策略,但这样做只是在本地为您改变情况。

无论你在本地更改了什么,尝试使用你的应用的任何其他人仍然会遇到同源策略,并且你无法为应用的其他用户禁用该策略。

你很可能永远不想使用模式:除了在少数有限的情况下,在实践中“no-cors”,即使这样,也只有在你确切地知道你在做什么以及效果是什么的情况下。这是因为设置实际上对浏览器说的是,“阻止我的前端JavaScript代码在任何情况下查看响应正文和标头的内容。在大多数情况下,这显然不是你想要的。mode: 'no-cors'


至于您希望考虑使用 的情况,请参阅不透明响应的哪些限制?中的答案,了解详细信息。它的要点是:mode: 'no-cors'

  • 在有限的情况下,当您使用 JavaScript 将来自另一个源的内容放入 、 、 、 或元素中时(这很有效,因为允许跨源嵌入资源)—但由于某种原因,您不希望/不能仅仅通过让文档的标记使用资源 URL 作为元素的 or 属性来做到这一点。<script><link rel=stylesheet><img><video><audio><object><embed><iframe>hrefsrc

  • 当您唯一要对资源执行的操作是缓存它时。正如在不透明响应适用哪些限制?中所述,实际上,该方案适用于使用服务工作线程的情况,在这种情况下,相关的 API 是缓存存储 API

但是,即使在这些有限的情况下,也有一些重要的事情需要注意;有关详细信息,请参阅哪些限制适用于不透明响应的答案?


我也尝试过传递对象{ mode: 'opaque'}

没有请求模式 - 只是响应的一个属性,浏览器在以模式发送的请求的响应上设置该不透明属性。'opaque'opaqueno-cors

但顺便说一句,不透明这个词是一个非常明确的信号,表明你最终得到的响应的性质:“不透明”意味着你看不到它的任何细节;它阻止你看到。


答案 2

因此,如果您像我一样在localhost上开发一个网站,您尝试从Laravel API获取数据并在Vue前端使用它,并且您看到此问题,以下是我如何解决它:

  1. 在 Laravel 项目中,运行 命令 。这将为您创建。php artisan make:middleware Corsapp/Http/Middleware/Cors.php
  2. 在 函数中添加以下代码:handlesCors.php

    return $next($request)
        ->header('Access-Control-Allow-Origin', '*')
        ->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
    
  3. 在 中,在数组中添加以下条目:app/Http/kernel.php$routeMiddleware

    ‘cors’ => \App\Http\Middleware\Cors::class
    

    (数组中还有其他条目,如 等。还要确保你正在这样做,因为在Laravel中还有另一个)authguestapp/Http/kernel.phpkernel.php

  4. 在路由注册时为要允许访问的所有路由添加此中间件,如下所示:

    Route::group(['middleware' => 'cors'], function () {
        Route::get('getData', 'v1\MyController@getData');
        Route::get('getData2', 'v1\MyController@getData2');
    });
    
  5. 在 Vue 前端中,请确保在函数中调用此 API,而不是在 中调用。此外,请确保在通话中使用或与 URL 一起使用。mounted()data()http://https://fetch()

皮特·休斯顿的博客文章的全部功劳。