尝试使用 fetch 和 pass in 模式:no-cors

Posted

技术标签:

【中文标题】尝试使用 fetch 和 pass in 模式:no-cors【英文标题】:Trying to use fetch and pass in mode: no-cors 【发布时间】:2017-09-01 21:27:05 【问题描述】:

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

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

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

请求中没有“Access-Control-Allow-Origin”标头 资源。因此不允许使用原点“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;
  );

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

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

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

【问题讨论】:

这能回答你的问题吗? Handle response - SyntaxError: Unexpected end of input when using mode: 'no-cors' 【参考方案1】:

mode: 'no-cors' 不会神奇地让事情顺利进行。事实上,它让事情变得更糟,因为它的一个作用是告诉浏览器,“在任何情况下都阻止我的前端 javascript 代码查看响应正文和标头的内容。” .

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

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

但是, 会起作用的一件事是:如果您通过 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

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

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

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

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

    将请求转发至https://example.com。 收到来自https://example.com的回复。 将Access-Control-Allow-Origin 标头添加到响应中。 将该响应连同添加的标头一起传递回请求的前端代码。

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

即使请求是触发浏览器执行 CORS 预检 OPTIONS 请求的请求,这也有效,因为在这种情况下,代理还会发回使预检成功所需的 Access-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 响应头。所以你必须改为使用代理来获取它。

代理向该站点发出请求,获取响应,添加 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 作为元素的hrefsrc 属性。

当您只想对资源进行缓存时。正如在 What limitations apply to opaque responses? 中提到的那样,实际上,当您使用 Service Worker 时,相关的 API 就是 Cache Storage API。

但即使在这些有限的情况下,也有一些重要的问题需要注意;请参阅 What limitations apply to opaque responses? 的答案以了解详细信息。


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

没有mode: 'opaque' 请求模式——opaque 只是响应 的一个属性,浏览器会根据no-cors 模式发送的请求的响应设置该不透明属性。

但顺便说一句,不透明这个词非常明确地表明了您最终得到的响应的性质:“不透明”意味着您看不到它。

【讨论】:

喜欢cors-anywhere 用于简单的非产品用例(即获取一些公开可用的数据)的解决方法。这个答案证实了我的怀疑,no-cors 并不常见,因为它的 OpaqueResponse 不是很有用;即“非常有限的情况”;任何人都可以explain to me 示例no-cors 有用吗? 我需要使用 CORS.js 包配置我的 Express 服务器 - github.com/expressjs/cors - 然后我需要从获取请求中删除 mode: 'no-cors'(否则响应将为空) CORS 代理很棒。我很惊讶它被认为是一种完全阻止访问公共文件的“安全”措施。从黑客的角度来看,它是如此、如此容易绕过,而这正是它的作用。对于好演员来说,这真的只是一个麻烦。 “阻止我的前端 JavaScript 代码在任何情况下查看响应正文和标头的内容。” - 对一个明显无用的“功能”的尖锐总结。感谢您将其拼写得如此清晰;我快疯了,想弄清楚它的用途是什么……【参考方案2】:

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

    在你的 Laravel 项目中,运行命令php artisan make:middleware Cors。这将为您创建app/Http/Middleware/Cors.php

    Cors.phphandles函数内添加以下代码:

    return $next($request)
        ->header('Access-Control-Allow-Origin', '*')
        ->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
    

    app/Http/kernel.php 中,在$routeMiddleware 数组中添加以下条目:

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

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

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

    Route::group(['middleware' => 'cors'], function () 
        Route::get('getData', 'v1\MyController@getData');
        Route::get('getData2', 'v1\MyController@getData2');
    );
    
    在 Vue 前端中,确保在 mounted() 函数中调用此 API,而不是在 data() 中。还要确保在fetch() 调用中使用http://https:// 和URL。

感谢 Pete Houston 的 blog article。

【讨论】:

【参考方案3】:

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

【讨论】:

我记得我的大学四年级课程,除了我们的小组之外,没有其他小组能做到这一点——奇怪的是,这是一个简单的解决方案。以下是更详尽的指南:stackabuse.com/handling-cors-with-node-js【参考方案4】:

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

用法很简单: 1。安装包: npm install -g local-ssl-proxy 2. 在运行 local-server 时,使用 local-ssl-proxy --source 9001 --target 9000 对其进行掩码

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

【讨论】:

我在真实手机设备中使用我的应用程序时遇到问题。您的解决方案对这种情况有帮助吗? @HamidAraghi 我想现在,因为出于开发目的,代理服务器应该在实际受错误影响的设备上运行 github.com/cameronhunter/local-ssl-proxy【参考方案5】:

对我来说,解决方案是在服务器端进行

我使用 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....
    )

【讨论】:

【参考方案6】:

如果您尝试在本地主机上临时解决此问题,您可以使用此 chrome 扩展:允许 CORS Access-Control-Allow-Origin https://chrome.google.com/webstore/detail/allow-cors-access-control/lhobafahddgcelffkeicbaginigeejlf

【讨论】:

【参考方案7】:

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

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

【讨论】:

如果您想要尽可能少的安全性,这是一个非常好的主意:)。 好主意,如果您控制源。 可怕的想法。首先,它只有在您可以访问源代码的情况下才有效,而 OP 没有,并且只有在您知道安全隐患的情况下才应该这样做。 问题是关于 javascript 请求。 这个很容易被攻击【参考方案8】:

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

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

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

文件夹:755

静态内容:644

动态内容:700

【讨论】:

【参考方案9】:

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

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

【讨论】:

【参考方案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);
  

【讨论】:

以上是关于尝试使用 fetch 和 pass in 模式:no-cors的主要内容,如果未能解决你的问题,请参考以下文章

Fetch in react native不发送帖子

使用 fetch 创建获取请求 - 仅发送选项

使用带有模式的 fetch API:'no-cors',无法设置请求标头

在将模式设置为“no-cors”时使用 fetch 访问 API 时出错 [重复]

Uncaught (in promise) SyntaxError: Unexpected token < in JSON at position 0 using basic fetches a

OFFSET N FETCH FIRST M ROWS 与 JDBC 和 PostgreSQL 不工作