【问题标题】:Trying to use fetch and pass in mode: no-cors尝试使用获取并传入模式:no-cors
【发布时间】:2022-12-04 03:23:19
【问题描述】:

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

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

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

请求中不存在“Access-Control-Allow-Origin”标头 资源。因此不允许来源 'http://localhost:3000' 使用权。如果不透明的响应满足您的需求,请设置请求的 mode 为 '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;
  });

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

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

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

【问题讨论】:

标签: javascript reactjs cors create-react-app fetch-api


【解决方案1】:

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

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

但是,如果站点在其响应中不发送 Access-Control-Allow-Origin,则您的前端代码无法直接访问该站点的响应。特别是,您不能通过指定 mode: 'no-cors' 来修复它(事实上,这会ensure您的前端代码无法访问响应内容)。

然而,有一件事将要工作:如果您通过a CORS proxy发送请求.

您还可以在 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

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

在您的请求 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 预检 OPTIONS 请求的请求,这也有效,因为在这种情况下,代理还会发回使预检成功所需的 Access-Control-Allow-HeadersAccess-Control-Allow-Methods 标头。


    我可以通过邮递员到达这个端点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 响应头。所以你必须改为使用代理来获取它。

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


    所以我试图将一个对象传递给我的 Fetch,这将禁用 CORS

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

    但无论如何,你确实可以——在你的本地环境中——做一些事情,比如给浏览器运行时标志来禁用安全和不安全地运行,或者你可以在本地安装浏览器扩展来绕过同源策略,但所做的只是在本地为您改变情况。

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

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


    至于你的情况想考虑使用mode: 'no-cors',查看答案What limitations apply to opaque responses?了解详情。它的要点是:

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

    • 当您唯一想对资源做的事情就是缓存它时。正如在中提到的What limitations apply to opaque responses?,在实践中,当你使用 Service Workers 的时候,相关的 API 是 Cache Storage API

    但即使在那些有限的情况下,也有一些重要的陷阱需要注意;看到答案What limitations apply to opaque responses?了解详情。


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

    没有 'opaque' 请求模式 — opaque 只是回复,并且浏览器在使用 no-cors 模式发送的请求的响应中设置不透明属性。

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

【讨论】:

  • 我需要使用 CORS.js 包配置我的 Express 服务器 - github.com/expressjs/cors - 然后我需要从获取请求中删除 mode: 'no-cors'(否则响应将为空)
  • 这是来自 Chrome 的一条非常愚蠢的错误消息:从源“localhost:8080”访问“quake.okayfun.com/maps/baseq3/index.json”已被 CORS 策略阻止:请求的资源上不存在“Access-Control-Allow-Origin”标头.如果不透明的响应满足您的需求,请将请求的模式设置为“no-cors”以在禁用 CORS 的情况下获取资源。
  • 您可以在通用 url http://cors-anywhere.herokuapp.com 前面加上通用 url,而不是运行本地 Heroku 应用程序
【解决方案2】:

如果您使用 Express 作为后端,您只需安装 cors 并在 app.use(cors()); 中导入和使用它。 如果未解决,请尝试切换端口。 切换端口后肯定会解决

【讨论】:

  • 我记得我的高年级大学班级,除了我们的班级,没有其他小组能做到这一点——奇怪的是,这是一个简单的解决方案。这是更详尽的指南:stackabuse.com/handling-cors-with-node-js
  • 这仅适用于您从自己的后端请求的情况吗?不是来自其他没有 Access-Control-Allow-Origin 标头集的 API 吗?或者你的意思是使用快速服务器作为代理?
【解决方案3】:

如果您试图在您的本地主机上暂时解决此问题,您可以使用此 chrome 扩展:允许 CORS 访问控制允许来源 https://chrome.google.com/webstore/detail/allow-cors-access-control/lhobafahddgcelffkeicbaginigeejlf

【讨论】:

    【解决方案4】:

    所以如果你像我一样在本地主机上开发一个网站,你试图从 Laravel API 获取数据并在你的 Vue 前端中使用它,并且你看到了这个问题,这就是我解决它的方法:

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

      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’ => AppHttpMiddlewareCors::class
      

      (数组中会有其他条目,如 authguest 等。还要确保你在 app/Http/kernel.php 中执行此操作,因为 Laravel 中还有另一个 kernel.php

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

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

      全部归功于 Pete Houston 的blog article

    【讨论】:

      【解决方案5】:

      如果您需要托管解决方案,您还可以设置一个反向代理,使用自托管的 CORS AnywhereJust CORS 添加 CORS 标头。

      https://justcors.com/<id>/<your-requested-resource>
      http://cors-anywhere.com/<your-requested-resource>
      

      【讨论】:

      • Cors-anywhere 似乎是更好的选择,因为它是永久的而不是临时的,justcors 必须每天更新。
      【解决方案6】:

      非常简单的解决方案(配置 2 分钟)是使用来自 npmlocal-ssl-proxy

      用法非常简单:
      1.安装包: npm install -g local-ssl-proxy
      2. 在运行你的local-server时用local-ssl-proxy --source 9001 --target 9000屏蔽它

      附言:--target 9000替换为-- "number of your port",将--source 9001替换为--source "number of your port +1"

      【解决方案7】:

      对我来说解决方案就是在服务器端做

      我使用 C# WebClient 库获取数据(在我的例子中是图像数据)并将其发送回客户端。在您选择的服务器端语言中可能有一些非常相似的东西。

      //Server side, api controller
      
      [Route("api/ItemImage/GetItemImageFromURL")]
      public IActionResult GetItemImageFromURL([FromQuery] string url)
      {
          ItemImage image = new ItemImage();
      
          using(WebClient client = new WebClient()){
      
              image.Bytes = client.DownloadData(url);
      
              return Ok(image);
          }
      }
      

      您可以根据自己的用例调整它。要点是 client.DownloadData() 没有任何 CORS 错误。通常 CORS 问题只发生在网站之间,因此可以从您的服务器发出“跨站点”请求。

      然后 React fetch 调用就像这样简单:

      //React component
      
      fetch(`api/ItemImage/GetItemImageFromURL?url=${imageURL}`, {            
              method: 'GET',
          })
          .then(resp => resp.json() as Promise<ItemImage>)
          .then(imgResponse => {
      
             // Do more stuff....
          )}
      

      【讨论】:

      • 我在使用流式 MP3 时遇到了同样的问题。它会播放,但我无法获取字节数组(如果存在某些 CORS 条件)。现在我想我可以在 c# 中执行此操作并自己发送它,但是将字节放在板上却无法使用它们太荒谬了。
      【解决方案8】:

      我的浏览器调试器也遇到了类似的问题,说我的 response.body 为空,但 fiddler 和开发人员工具显示它已填充,结果与此基本相同。我正在使用本地 Angular 应用程序访问在 IISExpress 上运行的 Web Api 服务。我按照 here 概述的步骤修复了它,以找到正确的 applicationhost.config 文件以添加 Access-Control-Allow-Origin 标头,如下所示:

        <customHeaders>
          <clear />
          <add name="X-Powered-By" value="ASP.NET" />
          <add name="Access-Control-Allow-Origin" value="*" />
          <add name="Access-Control-Allow-Headers" value="Content-Type" />
        </customHeaders>
      

      【讨论】:

        【解决方案9】:

        如果上述所有解决方案都不起作用,可能是因为文件权限,有时即使您使用 Heroku 或其他方式修复了非 cors 问题,它也会抛出 403 forbidden 错误。像这样设置目录/文件权限:

        权限和所有权错误 403 Forbidden 错误也可能是由于您的 Web 内容文件和文件夹的所有权或权限不正确造成的。

        权限 正确权限的经验法则:

        文件夹:755

        静态内容:644

        动态内容:700

        【讨论】:

          【解决方案10】:

          代码邮递员

          Javascript

          function fnlogin(){
             
          var url = ("http://localhost:3000/users/login");
          var method =('POST');
          var data = JSON.stringify({
              "email": "juan.perez@gmail.com",
              "password": "12345"
            });
          
            
            var xhr = new XMLHttpRequest();
            xhr.withCredentials = false; // SOLUTION False not True
            
            xhr.addEventListener("readystatechange", function () {
              if (this.readyState === 4) {
                console.log(this.responseText);
              }
            });
            
            xhr.open(method, url ,true);
            xhr.setRequestHeader("content-type", "application/json");
            xhr.setRequestHeader("cache-control", "no-cache");
            //xhr.send(data,{mode: 'no-cors'});
            xhr.send(data);
            }

          【讨论】:

            【解决方案11】:

            简单的解决方案:将以下内容添加到您要从中请求数据的 php 文件的最顶部。

            header("Access-Control-Allow-Origin: *");
            

            注意:此方法不安全,不推荐用于生产环境!

            【讨论】:

            • 如果您想要尽可能少的安全性,这是一个非常好的主意 :)。
            • 可怕的想法。首先,它只有在您有权访问源代码时才有效,而 OP 无法访问该源代码,并且只有在您了解安全隐患时才应该这样做。
            • 问题是关于 javascript 请求。
            • 这很容易受到攻击
            • CORS 验证不是在浏览器执行但并非绝对必需的飞行前完成的,这就是为什么邮递员可以从服务中获得结果而浏览器不能。如果是这样,为什么这是一个安全风险呢?如果我是一名黑客,我不会使用 firefox 来访问我正在黑客攻击的 Web 服务。如果这与安全无关,那么世界上为什么存在 CORS?
            猜你喜欢
            • 2017-09-01
            • 2018-08-27
            • 2019-09-13
            相关资源
            最近更新 更多