尝试从REST API获取数据时,请求的资源上不存在“Access-Control-Allow-Origin”标头 [英] No 'Access-Control-Allow-Origin' header is present on the requested resource—when trying to get data from a REST API

查看:177
本文介绍了尝试从REST API获取数据时,请求的资源上不存在“Access-Control-Allow-Origin”标头的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试从HP Alm的REST API中获取一些数据。它适用于小卷曲脚本 - 我得到了我的数据。

I'm trying to fetch some data from the REST API of HP Alm. It works pretty well with a small curl script - I get my data.

现在使用JavaScript,fetch和ES6(或多或少)这样做似乎是一个更大的问题。我不断收到此错误消息:

Now doing that with JavaScript, fetch and ES6 (more or less) seems to be a bigger issue. I keep getting this error message:


无法加载Fetch API。对预检请求的响应不是
通过访问控制检查:请求资源上没有'Access-Control-Allow-Origin'标头是
。 Origin' http://127.0.0.1:3000 '是
因此不允许访问。响应具有HTTP状态代码501.
如果不透明响应满足您的需要,请将请求的模式设置为
'no-cors'以获取禁用CORS的资源。

Fetch API cannot load . Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://127.0.0.1:3000' is therefore not allowed access. The response had HTTP status code 501. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

我知道这是因为我试图从我的localhost中获取该数据,解决方案应该是使用CORS。现在我以为我确实这样做了,但不管怎么说它要么忽略我在标题中写的内容,要么问题是别的什么?

I understand that this is because I am trying to fetch that data from within my localhost and the solution should be using CORS. Now I thought I actually did that, but somehow it either ignores what I write in the header or the problem is something else?

那么,是否存在实施问题?我做错了吗?遗憾的是我无法检查服务器日志。我真的有点卡在这里。

So, is there an implementation issue? Am I doing it wrong? I can't check the server logs unfortunately. I'm really a bit stuck here.

function performSignIn() {

  let headers = new Headers();

  headers.append('Content-Type', 'application/json');
  headers.append('Accept', 'application/json');

  headers.append('Access-Control-Allow-Origin', 'http://localhost:3000');
  headers.append('Access-Control-Allow-Credentials', 'true');

  headers.append('GET', 'POST', 'OPTIONS');

  headers.append('Authorization', 'Basic ' + base64.encode(username + ":" + password));

  fetch(sign_in, {
      //mode: 'no-cors',
      credentials: 'include',
      method: 'POST',
      headers: headers
    })
    .then(response => response.json())
    .then(json => console.log(json))
    .catch(error => console.log('Authorization failed : ' + error.message));
}

我正在使用Chrome。我也尝试使用Chrome CORS插件,但后来我收到另一条错误消息:

I am using Chrome. I also tried using that Chrome CORS Plugin, but then I am getting another error message:


'Access-Control-Allow的值 - 当请求的凭据模式为
'include'时,响应
中的原始标题不能是通配符'*'。因此,不允许
访问来源' http://127.0.0.1:3000 '。
XMLHttpRequest发起的请求的凭据模式由withCredentials属性控制。

The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. Origin 'http://127.0.0.1:3000' is therefore not allowed access. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.


推荐答案

这个答案涵盖了很多方面,因此它分为三个部分:

This answer covers a lot of ground, so it’s divided into three parts:


  • 如何使用CORS代理来绕过No Access-Control-Allow-Origin header问题

  • 如何避免CORS预检

  • 如何修复Access-Control-Allow-Origin标头不能是通配符问题

  • How to use a CORS proxy to get around "No Access-Control-Allow-Origin header" problems
  • How to avoid the CORS preflight
  • How to fix "Access-Control-Allow-Origin header must not be the wildcard" problems

如何使用CORS代理绕过No Access-Control-Allow-Origin标头问题

How to use a CORS proxy to get around "No Access-Control-Allow-Origin header" problems

如果您不控制服务器,您的前端JavaScript代码正在向其发送请求,而该服务器的响应问题只是缺少必要的 Access-Control- Allow-Origin 标题,您仍然可以通过制作来实现目标请求通过CORS代理。为了说明它是如何工作的,首先是一些不使用CORS代理的代码:

If you don’t control the server your frontend JavaScript code is sending a request to, and the problem with the response from that server is just the lack of the necessary Access-Control-Allow-Origin header, you can still get things to work—by making the request through a CORS proxy. To show how that works, first here’s some code that doesn’t use a CORS proxy:

const url = "https://example.com"; // site that doesn’t send Access-Control-*
fetch(url)
.then(response => response.text())
.then(contents => console.log(contents))
.catch(() => console.log("Can’t access " + url + " response. Blocked by browser?"))

catch 块遭遇命中的原因是,浏览器阻止该代码访问从<$返回的响应C $ C> https://example.com 。浏览器这样做的原因是,响应缺少 Access-Control-Allow-Origin 响应标头。

The reason the catch block gets hit there is, the browser prevents that code from accessing the response which comes back from https://example.com. And the reason the browser does that is, the response lacks the Access-Control-Allow-Origin response header.

现在,这是完全相同的示例,但只添加了一个CORS代理:

Now, here’s exactly the same example but just with a CORS proxy added in:

const proxyurl = "https://cors-anywhere.herokuapp.com/";
const url = "https://example.com"; // site that doesn’t send Access-Control-*
fetch(proxyurl + url) // https://cors-anywhere.herokuapp.com/https://example.com
.then(response => response.text())
.then(contents => console.log(contents))
.catch(() => console.log("Can’t access " + url + " response. Blocked by browser?"))

注意:如果https:// cors- anywhere.herokuapp.com在您尝试时已关闭或不可用,请参阅下文,了解如何在Heroku内部署您自己的CORS Anywhere服务器仅需2-3分钟。

Note: If https://cors-anywhere.herokuapp.com is down or unavailable when you try it, then see below for how to deploy your own CORS Anywhere server at Heroku in just 2-3 minutes.

上面的第二个代码片段可以成功访问响应,因为获取请求URL并将其更改为 https://cors-anywhere.herokuapp.com/https://example.com - 只需在其前面添加代理URL,即可通过该代理进行请求,然后:

The second code snippet above can access the response successfully because taking the request URL and changing it to https://cors-anywhere.herokuapp.com/https://example.com—by just prefixing it with the proxy URL—causes the request to get made through that proxy, which then:


  1. 将请求转发给 https:// exampl e.com

  2. https://example.com 收到回复。

  3. Access-Control-Allow-Origin 标头添加到响应中。

  4. 传递响应,使用该添加的标题,返回到请求的前端代码。

  1. Forwards the request to https://example.com.
  2. Receives the response from https://example.com.
  3. Adds the Access-Control-Allow-Origin header to the response.
  4. Passes that response, with that added header, back to the requesting frontend code.

然后,浏览器允许前端代码访问响应,因为该响应使用 Access-Control-Allow-Origin 响应标题是浏览器看到的内容。

The browser then allows the frontend code to access the response, because that response with the Access-Control-Allow-Origin response header is what the browser sees.

您可以轻松运行使用 https://github.com/Rob--W/cors-anywhere中的代码自己代理/

您还可以轻松地将自己的代理部署到Heroku,只需2-3分钟,有5个命令:

You can easily run your own proxy using code from https://github.com/Rob--W/cors-anywhere/.
You can also easily deploy your own proxy to Heroku in literally just 2-3 minutes, with 5 commands:

git clone https://github.com/Rob--W/cors-anywhere.git
cd cors-anywhere/
npm install
heroku create
git push heroku master

运行这些co之后mmands,你最终会得到你自己运行的CORS Anywhere服务器,例如 https:// cryptic- headland-94862.herokuapp.com/ 。那么,不要在您的请求URL前加上 https://cors-anywhere.herokuapp.com ,而是在前缀中添加您自己实例的URL;例如, https://cryptic-headland-94862.herokuapp.com/https ://example.com

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.

因此,如果您尝试使用https://cors-anywhere.herokuapp.com,你发现它已经失效(它有时会是这样),然后考虑获得一个Heroku帐户(如果你还没有)并花2或3分钟来完成上述步骤在Heroku上部署你自己的CORS Anywhere服务器。

So if when you go to try to use https://cors-anywhere.herokuapp.com, you find it’s down (which it sometimes will be), then consider getting a Heroku account (if you don’t already) and take 2 or 3 minutes to do the steps above to deploy your own CORS Anywhere server on Heroku.

无论你是自己经营还是使用 https: //cors-anywhere.herokuapp.com 或其他开放代理,即使请求是触发浏览器执行CORS预检的请求,此解决方案仍然有效 OPTIONS 请求 - 因为在这种情况下,代理还发送回 Access-Control-Allow-Headers Access-Control-Allow-Methods 使预检成功所需的标题。

Regardless, whether you run your own or use https://cors-anywhere.herokuapp.com or other open proxy, this solution will work even if the request is one that triggers browsers to do a CORS preflight OPTIONS request—because in that case, the proxy also sends back the Access-Control-Allow-Headers and Access-Control-Allow-Methods headers needed to make the preflight successful.

如何避免CORS预检

问题中的代码触发了CORS预检 - 因为它发送授权标题。

The code in the question triggers a CORS preflight—since it sends an Authorization header.

https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Preflighted_requests

即使没有这个, Content-Type:application / json 标题也会触发预检。

Even without that, the Content-Type: application/json header would also trigger the preflight.

预检是什么意思:在浏览器在问题代码中尝试 POST 之前,它会先发送一个选项请求服务器 - 确定服务器是否选择接收包含交叉来源 POST 的服务器授权 Content-Type:application / json 标题。

What "preflight" means: before the browser tries the POST in the code in the question, it’ll first send an OPTIONS request to the server — to determine if the server is opting-in to receiving a cross-origin POST that includes the Authorization and Content-Type: application/json headers.


它适用于小型l卷曲脚本 - 我得到我的数据。

It works pretty well with a small curl script - I get my data.

正确测试卷曲 ,您需要模拟预检 c> 请求浏览器发送:

To properly test with curl, you need to emulate the preflight OPTIONS request the browser sends:

curl -i -X OPTIONS -H "Origin: http://127.0.0.1:3000" \
    -H 'Access-Control-Request-Method: POST' \
    -H 'Access-Control-Request-Headers: Content-Type, Authorization' \
    "https://the.sign_in.url"

...以 https://the.sign_in.url 替换为您的实际 sign_in URL是。

…with https://the.sign_in.url replaced by whatever your actual sign_in URL is.

浏览器需要从 OPTIONS 请求中看到的响应必须包含以下标题:

The response the browser needs to see from that OPTIONS request must include headers like this:

Access-Control-Allow-Origin:  http://127.0.0.1:3000
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: Content-Type, Authorization

如果 OPTIO NS 响应不包括那些标题,然后浏览器将停在那里,甚至从未尝试发送 POST 请求。此外,响应的HTTP状态代码必须是2xx - 通常为200或204.如果是任何其他状态代码,浏览器将在那里停止。

If the OPTIONS response doesn’t include those headers, then the browser will stop right there and never even attempt to send the POST request. Also, the HTTP status code for the response must be a 2xx—typically 200 or 204. If it’s any other status code, the browser will stop right there.

服务器问题是回复带有501状态代码的 OPTIONS 请求,这显然意味着它试图表明它没有实现对 OPTIONS <的支持/ code>请求。在这种情况下,其他服务器通常会响应405Method not allowed状态代码。

The server in the question is responding to the OPTIONS request with a 501 status code, which apparently means it’s trying to indicate it doesn’t implement support for OPTIONS requests. Other servers typically respond with a 405 "Method not allowed" status code in this case.

所以你永远无法做出 POST 如果服务器使用405或501或200以外的任何其他方式响应 OPTIONS 请求,则从前端JavaScript代码直接向该服务器发出请求或204或如果没有回复那些必要的响应标题。

So you’re never going to be able to make POST requests directly to that server from your frontend JavaScript code if the server responds to that OPTIONS request with a 405 or 501 or anything other than a 200 or 204 or if doesn’t respond with those necessary response headers.

避免在问题中触发案件预检的方法是:

The way to avoid triggering a preflight for the case in the question would be:


  • 如果服务器不需要授权请求标头,而是(例如)依赖于身份验证嵌入在 POST 请求正文中的数据或作为查询参数

  • 如果服务器不需要 POST 正文以 Content-Type:application / json 媒体类型,但接受 POST body as application / x-www-form-urlencoded with para名为 json 的仪表(或其他),其值为JSON数据

  • if the server didn’t require an Authorization request header but instead (for example) relied on authentication data embedded in the body of the POST request or as a query parameter
  • if the server didn’t require the POST body to have a Content-Type: application/json media type but instead accepted the POST body as application/x-www-form-urlencoded with a parameter named json (or whatever) whose value is the JSON data

如何修复Access-Control-Allow-Origin标头不能是通配符问题

How to fix "Access-Control-Allow-Origin header must not be the wildcard" problems


我收到另一条错误消息:

I am getting another error message:


'Access'的值当请求的凭据模式为
'include'时,响应
中的-Control-Allow-Origin'标头不能是通配符'*'。因此,不允许
访问来源' http://127.0.0.1:3000 '。
XMLHttpRequest发起的请求的凭据模式由withCredentials属性控制。

The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. Origin 'http://127.0.0.1:3000' is therefore not allowed access. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.


对于包含凭据的请求,如果 Access-Control-Allow-Origin 响应标头的值<$,浏览器将不允许您的前端JavaScript代码访问响应C $ C> * 。相反,该情况下的值必须与您的前端代码的来源完全匹配, http://127.0.0.1:3000

For a request that includes credentials, browsers won’t let your frontend JavaScript code access the response if the value of the Access-Control-Allow-Origin response header is *. Instead the value in that case must exactly match your frontend code’s origin, http://127.0.0.1:3000.

请参阅 Credentialed request and wildcards 在MDN HTTP访问控制(CORS)文章中。

See Credentialed requests and wildcards in the MDN HTTP access control (CORS) article.

如果您控制要发送请求的服务器,那么处理此问题的常用方法case是配置服务器获取 Origin 请求标头的值,并将其回显/反映回 Access-Control-的值Allow-Origin 响应头。例如,使用nginx:

If you control the server you’re sending the request to, then a common way to deal with this case is to configure the server to take the value of the Origin request header, and echo/reflect that back into the value of the Access-Control-Allow-Origin response header. For example, with nginx:

add_header Access-Control-Allow-Origin $http_origin

但这只是一个例子;其他(网络)服务器系统提供类似的方式来回显原始值。

But that’s just one example; other (web) server systems provide similar ways to echo origin values.


我正在使用铬。我还尝试使用Chrome CORS插件

I am using Chrome. I also tried using that Chrome CORS Plugin

Chrome CORS插件显然只是简单地注入 Access-Control- Allow-Origin:* 标题到浏览器看到的响应中。如果插件更聪明,它将要做的是将假的 Access-Control-Allow-Origin 响应头的值设置为前端JavaScript代码的实际原点, http://127.0.0.1:3000

That Chrome CORS plugin apparently just simplemindedly injects an Access-Control-Allow-Origin: * header into the response the browser sees. If the plugin were smarter, what it would be doing is setting the value of that fake Access-Control-Allow-Origin response header to the actual origin of your frontend JavaScript code, http://127.0.0.1:3000.

因此,请避免使用该插件,即使是测试也是如此。这只是一种分心。如果您想测试从服务器获得的响应而没有浏览器过滤它们,那么最好使用 curl -H ,如上所述。

So avoid using that plugin, even for testing. It’s just a distraction. If you want to test what responses you get from the server with no browser filtering them, you’re better off using curl -H as above.

至于问题中 fetch(...)请求的前端JavaScript代码:

As far as the frontend JavaScript code for the fetch(…) request in the question:

headers.append('Access-Control-Allow-Origin', 'http://localhost:3000');
headers.append('Access-Control-Allow-Credentials', 'true');

删除这些行。 Access-Control-Allow - * 标头是响应标头。您永远不想在请求中发送它们。唯一的影响就是触发浏览器进行预检。

Remove those lines. The Access-Control-Allow-* headers are response headers. You never want to send them in a request. The only effect that’ll have is to trigger a browser to do a preflight.

这篇关于尝试从REST API获取数据时,请求的资源上不存在“Access-Control-Allow-Origin”标头的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆