尝试使用获取和通过模式:无心


166

我可以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。我想念什么?

Answers:


289

mode: 'no-cors'不会神奇地使事情起作用。实际上,这使情况变得更糟,因为它的作用之一是告诉浏览器:“在任何情况下都禁止我的前端JavaScript代码查看响应主体和标头的内容。” 当然,您几乎永远都不需要。

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

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

但是,这起作用:如果您通过CORS代理发送请求,如下所示:

var proxyUrl = 'https://cors-anywhere.herokuapp.com/',
    targetUrl = 'http://catfacts-api.appspot.com/api/facts?number=99'
fetch(proxyUrl + targetUrl)
  .then(blob => blob.json())
  .then(data => {
    console.table(data);
    document.querySelector("pre").innerHTML = JSON.stringify(data, null, 2);
    return data;
  })
  .catch(e => {
    console.log(e);
    return e;
  });
<pre></pre>

注意:如果您尝试使用https://cors-anywhere.herokuapp.com时发现它已关闭,则还可以使用5条命令在2-3分钟内轻松地将自己的代理部署到Heroku中:

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前面加上https://cors-anywhere.herokuapp.com,而是在您自己的实例的URL 前面加上前缀;例如https://cryptic-headland-94862.herokuapp.com/https://example.com


我可以http://catfacts-api.appspot.com/api/facts?number=99通过邮递员达到这个终点

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

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'除了极少数情况下您极有可能永远不想在实践中使用,甚至只有在您确切知道自己在做什么和产生什么影响的情况下才可以在实践中使用它。这是因为设置mode: 'no-cors'对浏览器的实际含义是:“在任何情况下都禁止我的前端JavaScript代码查看响应正文和标头的内容。” 在大多数情况下,这显然不是您想要的。


至于情况下,您考虑使用mode: 'no-cors',请在答案限制适用于不透明的反应是什么?有关详细信息。其要点是:

  • 在你使用JavaScript来使资源从另一个原点的内容时,有限的情况下<script><link rel=stylesheet><img><video><audio><object><embed>,或<iframe>元素(它的作品,因为资源的嵌入跨域允许那些) -但由于某种原因您不希望或者不能仅仅通过使文档的标记使用资源URL作为元素的hrefor src属性来做到这一点。

  • 当您只想对资源进行缓存时。正如答案中提到的那样,对不透明响应有哪些限制?,实际上,适用的情况是使用Service Workers时,在这种情况下,相关的API是Cache Storage API

但是即使在那些有限的情况下,也要注意一些重要的陷阱。请参见以下答案:不透明响应有哪些限制?有关详细信息。


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

没有mode: 'opaque'请求模式- opaque而是仅是response的属性,浏览器在以该no-cors模式发送的请求的响应上设置了该不透明属性。

但是顺便说一句,“ 不透明 ”一词是一个非常明确的信号,表示您最终得到的响应的性质:“不透明”表示您看不到它。


4
喜欢cors-anywhere简单的非产品用例(例如,获取一些公开可用的数据)的解决方法。这个答案证实了我no-cors的普遍性,因为它的OpaqueResponse不是很有用。即“非常有限的情况”;谁能向我解释一些no-cors有用的例子?
红豌豆

1
@TheRedPea请参阅我在此处对答案所做的更新(并且我也在stackoverflow.com/questions/52569895/…上添加了对您的问题的评论)
sideshowbarker

我需要配置我的Express服务器与CORS.js包- github.com/expressjs/cors -然后我需要删除mode: 'no-cors'从取得请求(否则反应会是空的)
詹姆斯·

CORS代理很棒。令我惊讶的是,它完全被视为阻止访问公共文件的“安全”措施。这样,从黑客的角度出发很容易解决,这正是这样做的目的。对于好演员来说,这实际上只是一个麻烦。
Seph Reed

我生成使用相同API的不同客户端的代码。我用它来训练。我想保持代码简单,让用户使用它。我需要使用无固定资产。
profimedica,

6

因此,如果您像我一样,并且在localhost上开发一个网站,尝试从Laravel API中获取数据并在您的Vue前端中使用它,那么您会看到此问题,这是我解决的方法:

  1. 在您的Laravel项目中,运行command php artisan make:middleware Cors。这将为app/Http/Middleware/Cors.php您创建。
  2. 在的handles函数内添加以下代码Cors.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

    (数组中还会有其他条目,例如authguest等等。还请确保您正在执行此操作,app/Http/kernel.php因为kernel.phpLaravel中也有另一个条目)

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

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

完全归功于Pete Houston的博客文章


3

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

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


6
如果您想要尽可能少的安全性,这是一个非常好的主意:)。
Heretic Monkey”,

图像热链接怎么样
user889030

1

我的解决方案是只在服务器端执行此操作

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

1

一个非常简单的解决方案(配置需要2分钟)是使用来自以下位置的local-ssl-proxy软件包:npm

用法很简单:
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"


1
我在真实电话设备上使用我的应用程序时遇到问题。您的解决方案对这种情况有帮助吗?
哈米德·阿拉基

@HamidAraghi我现在想,因为出于开发目的,代理服务器应该在实际上受该错误影响的设备上运行
volna
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.