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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/43262121/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 01:47:34  来源:igfitidea点击:

Trying to use fetch and pass in mode: no-cors

javascriptreactjscorscreate-react-appfetch-api

提问by dwww

I can hit this endpoint, http://catfacts-api.appspot.com/api/facts?number=99via Postman and it returns JSON

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

Additionally I am using create-react-app and would like to avoid setting up any server config.

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

In my client code I am trying to use fetchto do the same thing, but I get the error:

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

No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

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

So I am trying to pass in an object, to my Fetch which will disable CORS, like so:

所以我试图将一个对象传递给我的 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;
  });

Interestingly enough the error I get is actually a syntax error with this function. I am not sure my actual fetchis broken, because when I remove the { mode: 'no-cors' } object, and supply it with a different URL it works just fine.

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

I have also tried to pass in the object { mode: 'opaque'}, but this returns the original error from above.

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

I belive all I need to do is disable CORS.. What am I missing?

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

回答by sideshowbarker

mode: 'no-cors'won't magically make things work. In fact it makes things worse, because one effect it has is to tell browsers, “Block my frontend JavaScript code from looking at contents of the response body and headers under all circumstances.”Of course you almost never want that.

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

What happens with cross-origin requests from frontend JavaScript is that browsers by default block frontend code from accessing resources cross-origin. If Access-Control-Allow-Originis in a response, then browsers will relax that blocking and allow your code to access the response.

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

But if a site sends no Access-Control-Allow-Originin its responses, your frontend code can't directly access responses from that site. In particular, you can't fix it by specifying mode: 'no-cors'(in fact that'll ensureyour frontend code can't access the response contents).

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

However, one thing that willwork: if you send your request through a CORS proxy, like this:

然而,有一件事是工作:如果您通过发送请求一个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>

Note: if when you go to try to use https://cors-anywhere.herokuapp.com, you find it's down, you can also easily deploy your own proxy to Heroku in literally just 2-3 minutes, with 5 commands:

注意:如果当您尝试使用 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

After running those commands, you'll end up with your own CORS Anywhere server running at, e.g., https://cryptic-headland-94862.herokuapp.com/. So then rather than prefixing your request URL with https://cors-anywhere.herokuapp.com, prefix it instead with the URL for your own instance; e.g., https://cryptic-headland-94862.herokuapp.com/https://example.com.

运行这些命令后,您最终将拥有自己的 CORS Anywhere 服务器,例如在https://cryptic-headland-94862.herokuapp.com/ 上运行。因此,与其在您的请求 URL 前面加上https://cors-anywhere.herokuapp.com前缀,不如在它前面加上您自己实例的 URL;例如,https: //cryptic-headland-94862.herokuapp.com/https: //example.com



I can hit this endpoint, http://catfacts-api.appspot.com/api/facts?number=99via Postman

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

https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORSexplains why it is that even though you can access the response with Postman, browsers won't let you access the response cross-origin from frontend JavaScript code running in a web app unless the response includes an Access-Control-Allow-Originresponse header.

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

http://catfacts-api.appspot.com/api/facts?number=99has no Access-Control-Allow-Originresponse header, so there's no way your frontend code can access the response cross-origin.

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

Your browser can get the response fine and you can see it in Postman and even in browser devtools—but that doesn't mean browsers will expose it to your code. They won't, because it has no Access-Control-Allow-Originresponse header. So you must instead use a proxy to get it.

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

The proxy makes the request to that site, gets the response, adds the Access-Control-Allow-Originresponse header and any other CORS headers needed, then passes that back to your requesting code. And that response with the Access-Control-Allow-Originheader added is what the browser sees, so the browser lets your frontend code actually access the response.

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



So I am trying to pass in an object, to my Fetch which will disable CORS

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

You don't want to do that. To be clear, when you say you want to “disable CORS” it seems you actually mean you want to disable the same-origin policy. CORS itself is actually a way to do that — CORS is a way to loosen the same-origin policy, not a way to restrict it.

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

But anyway, it's true you can — in just your local environment —?do things like give your browser runtime flags to disable security and run insecurely, or you can install a browser extension locally to get around the same-origin policy, but all that does is change the situation just for you locally.

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

No matter what you change locally, anybody else trying to use your app is still going to run into the same-origin policy, and there's no way you can disable that for other users of your app.

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

You most likely never want to use mode: 'no-cors'in practice except in a few limited cases, and even then only if you know exactly what you're doing and what the effects are. That's because what setting mode: 'no-cors'actually says to the browser is, “Block my frontend JavaScript code from looking into the contents of the response body and headers under all circumstances.”In most cases that's obviously really not what you want.

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



As far as the cases when you wouldwant to consider using mode: 'no-cors', see the answer at What limitations apply to opaque responses?for the details. The gist of it is that the cases are:

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

  • In the limited case when you're using JavaScript to make a resource from another origin the content of 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 or can't do that just by having the markup of the document use the resource URL as the hrefor srcattribute for the element.

  • When the only thing you want to do with a resource is to cache it. As alluded to in the answer at What limitations apply to opaque responses?, in practice the scenario that applies to is when you're using Service Workers, in which case the API that's relevant is the Cache Storage API.

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

  • 当您想要对资源做的唯一事情就是缓存它时。正如在哪些限制适用于不透明响应?,实际上适用的场景是当您使用 Service Workers 时,在这种情况下,相关的 API 是Cache Storage API

But even in those limited cases, there are some important gotchas to be aware of;see the answer at What limitations apply to opaque responses?for the details.

但即使在这些有限的情况下,也有一些重要的问题需要注意;请参阅哪些限制适用于不透明响应?详情。



I have also tried to pass in the object { mode: 'opaque'}

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

There is no mode: 'opaque'request mode — opaqueis instead just a property of the response, and browsers set that opaque property on responses from requests sent with the no-corsmode.

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

But incidentally the word opaqueis a pretty explicit signal about the nature of the response you end up with: “opaque” means you can't see it.

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

回答by dotNET

So if you're like me and developing a website on localhost where you're trying to fetch data from Laravel API and use it in your Vue front-end, and you see this problem, here is how I solved it:

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

  1. In your Laravel project, run command php artisan make:middleware Cors. This will create app/Http/Middleware/Cors.phpfor you.
  2. Add the following code inside the handlesfunction in Cors.php:

    return $next($request)
        ->header('Access-Control-Allow-Origin', '*')
        ->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
    
  3. In app/Http/kernel.php, add the following entry in $routeMiddlewarearray:

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

    (There would be other entries in the array like auth, guestetc. Also make sure you're doing this in app/Http/kernel.phpbecause there is another kernel.phptoo in Laravel)

  4. Add this middleware on route registration for all the routes where you want to allow access, like this:

    Route::group(['middleware' => 'cors'], function () {
        Route::get('getData', 'v1\MyController@getData');
        Route::get('getData2', 'v1\MyController@getData2');
    });
    
  5. In Vue front-end, make sure you call this API in mounted()function and not in data(). Also make sure you use http://or https://with the URL in your fetch()call.
  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.php太Laravel)

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

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

Full credits to Pete Houston's blog article.

全部归功于皮特休斯顿的博客文章

回答by Really Nice Code

The simple solution:Add the following to the very top of the php file you are requesting the data from.

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

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

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

回答by Stuart Aitken

Solution for me was to just do it server side

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

I used the C# WebClientlibrary to get the data (in my case it was image data) and send it back to the client. There's probably something very similar in your chosen server-side language.

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

You can tweak it to whatever your own use case is. The main point is client.DownloadData()worked without any CORS errors. Typically CORS issues are only between websites, hence it being okay to make 'cross-site' requests from your server.

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

Then the React fetch call is as simple as:

然后 React fetch 调用很简单:

//React component

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

       // Do more stuff....
    )}

回答by volna

Very easy solution (2 min to config) is to use local-ssl-proxypackage from npm

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

The usage is straight pretty forward:
1. Install the package: npm install -g local-ssl-proxy
2. While running your local-servermask it with the local-ssl-proxy --source 9001 --target 9000

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

P.S:Replace --target 9000with the -- "number of your port"and --source 9001with --source "number of your port +1"

PS:更换--target 9000-- "number of your port"--source 9001--source "number of your port +1"