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

此外,我正在使用创建-反应-应用程序,并希望避免设置任何服务器配置。

在我的客户端代码中,我试图使用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;
  });

有趣的是,我得到的错误实际上是这个函数的语法错误。我不确定我的实际获取是坏的,因为当我删除{mode: 'no-cors'}对象,并提供了一个不同的URL,它工作得很好。

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

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


当前回答

模式:“no-cors”不会神奇地让事情起作用。事实上,它使事情变得更糟,因为它的一个效果是告诉浏览器,“在任何情况下都阻止我的前端JavaScript代码看到响应体和报头的内容。”当然你永远都不想那样。

来自前端JavaScript的跨源请求是浏览器默认阻止前端代码访问跨源资源。如果在响应中有access - control - allow - origin,那么浏览器会放松阻塞,并允许您的代码访问响应。

但是如果一个站点在它的响应中没有发送access - control - allow - origin,你的前端代码就不能直接访问该站点的响应。特别是,你不能通过指定mode: 'no-cors'来修复它(事实上,这将确保你的前端代码不能访问响应内容)。

然而,有一件事是可行的:如果你通过CORS代理发送请求。

您还可以轻松地部署自己的代理到Heroku,只需2-3分钟,只需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 preflight OPTIONS请求的请求,这也可以工作,因为在这种情况下,代理还会发回使preflight成功所需的Access-Control-Allow-Headers和Access-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甚至在浏览器devtools中看到它,但这并不意味着浏览器将它暴露给您的代码。它们不会,因为它没有Access-Control-Allow-Origin响应头。因此,您必须使用代理来获取它。

代理向该站点发出请求,获得响应,添加Access-Control-Allow-Origin响应标头和所需的任何其他CORS标头,然后将其传递回请求代码。加上access - control - allow - origin头的响应是浏览器看到的,所以浏览器让前端代码实际访问响应。


我试图传递一个对象给Fetch,它会禁用CORS

你不会想这么做的。需要明确的是,当您说要“禁用CORS”时,似乎实际上是指要禁用同源策略。CORS本身实际上是一种实现这一目标的方式——CORS是一种放松同源政策的方式,而不是一种限制政策的方式。

但无论如何,确实可以在本地环境中设置浏览器运行时标志来禁用安全性并不安全地运行,或者可以在本地安装浏览器扩展来绕过同源策略,但所有这些都只是在本地改变了情况。

无论你在本地做了什么改变,任何试图使用你的应用的人仍然会遇到同源策略,你没有办法为你的应用的其他用户禁用它。

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


至于你想要考虑使用mode: 'no-cors'的情况,请参阅What limits apply to opaque responses?详情如下。它的要点是:

In the limited case when you’re using JavaScript to put content from another origin into a <script>, <link rel=stylesheet>, <img>, <video>, <audio>, <object>, <embed>, or <iframe> element (which works because embedding of resources cross-origin is allowed for those)—but for some reason you don’t want to/can’t do that just by having the markup of the document use the resource URL as the href or src attribute for the element. When the only thing you want to do with a resource is to cache it. As alluded to in What limitations apply to opaque responses?, in practice the scenario that’s for is when you’re using Service Workers, in which case the API that’s relevant is the Cache Storage API.

但即使在这些有限的情况下,也有一些重要的陷阱需要注意;看答案在什么限制适用于不透明的回应?详情如下。


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

不存在“opaque”请求模式——opaque只是响应的一个属性,浏览器会在以no-cors模式发送的请求的响应上设置该opaque属性。

但顺便提一下,“opaque”这个词是一个非常明确的信号,表明了你最终得到的回答的性质:“opaque”意味着你看不到它的任何细节;它挡住了你的视线。

其他回答

模式:“no-cors”不会神奇地让事情起作用。事实上,它使事情变得更糟,因为它的一个效果是告诉浏览器,“在任何情况下都阻止我的前端JavaScript代码看到响应体和报头的内容。”当然你永远都不想那样。

来自前端JavaScript的跨源请求是浏览器默认阻止前端代码访问跨源资源。如果在响应中有access - control - allow - origin,那么浏览器会放松阻塞,并允许您的代码访问响应。

但是如果一个站点在它的响应中没有发送access - control - allow - origin,你的前端代码就不能直接访问该站点的响应。特别是,你不能通过指定mode: 'no-cors'来修复它(事实上,这将确保你的前端代码不能访问响应内容)。

然而,有一件事是可行的:如果你通过CORS代理发送请求。

您还可以轻松地部署自己的代理到Heroku,只需2-3分钟,只需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 preflight OPTIONS请求的请求,这也可以工作,因为在这种情况下,代理还会发回使preflight成功所需的Access-Control-Allow-Headers和Access-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甚至在浏览器devtools中看到它,但这并不意味着浏览器将它暴露给您的代码。它们不会,因为它没有Access-Control-Allow-Origin响应头。因此,您必须使用代理来获取它。

代理向该站点发出请求,获得响应,添加Access-Control-Allow-Origin响应标头和所需的任何其他CORS标头,然后将其传递回请求代码。加上access - control - allow - origin头的响应是浏览器看到的,所以浏览器让前端代码实际访问响应。


我试图传递一个对象给Fetch,它会禁用CORS

你不会想这么做的。需要明确的是,当您说要“禁用CORS”时,似乎实际上是指要禁用同源策略。CORS本身实际上是一种实现这一目标的方式——CORS是一种放松同源政策的方式,而不是一种限制政策的方式。

但无论如何,确实可以在本地环境中设置浏览器运行时标志来禁用安全性并不安全地运行,或者可以在本地安装浏览器扩展来绕过同源策略,但所有这些都只是在本地改变了情况。

无论你在本地做了什么改变,任何试图使用你的应用的人仍然会遇到同源策略,你没有办法为你的应用的其他用户禁用它。

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


至于你想要考虑使用mode: 'no-cors'的情况,请参阅What limits apply to opaque responses?详情如下。它的要点是:

In the limited case when you’re using JavaScript to put content from another origin into a <script>, <link rel=stylesheet>, <img>, <video>, <audio>, <object>, <embed>, or <iframe> element (which works because embedding of resources cross-origin is allowed for those)—but for some reason you don’t want to/can’t do that just by having the markup of the document use the resource URL as the href or src attribute for the element. When the only thing you want to do with a resource is to cache it. As alluded to in What limitations apply to opaque responses?, in practice the scenario that’s for is when you’re using Service Workers, in which case the API that’s relevant is the Cache Storage API.

但即使在这些有限的情况下,也有一些重要的陷阱需要注意;看答案在什么限制适用于不透明的回应?详情如下。


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

不存在“opaque”请求模式——opaque只是响应的一个属性,浏览器会在以no-cors模式发送的请求的响应上设置该opaque属性。

但顺便提一下,“opaque”这个词是一个非常明确的信号,表明了你最终得到的回答的性质:“opaque”意味着你看不到它的任何细节;它挡住了你的视线。

我有一个类似的问题,我的浏览器调试器说我的响应。Body为null,但fiddler和开发人员工具显示它已填充,结果基本上与此相同的场景。我正在使用一个本地Angular应用程序访问运行在IISExpress上的Web Api服务。我通过遵循这里列出的步骤来找到正确的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>

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

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

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

文件夹:755

静态内容:644

动态内容:700

我的解决方案是在服务器端进行

我使用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获取调用就像这样简单:

//React component

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

       // Do more stuff....
    )}

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

用法很直接: 1. 安装包: NPM install -g local-ssl-proxy 2. 在运行local-server时,使用local-ssl-proxy——source 9001——target 9000进行掩码

注:将——目标9000替换为——“您的端口编号”,将——源9001替换为——源“您的端口编号+1”