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

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

问题描述

我正在尝试从 HP Alm 的 REST API 获取一些数据.它与一个小的 curl 脚本配合得很好 - 我得到了我的数据.

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"标头出现在请求的资源上.来源 '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.

我知道这是因为我试图从我的本地主机中获取该数据并且解决方案应该使用 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-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.

推荐答案

这个答案涵盖了很多方面,所以分为三个部分:

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 标头不能是通配符" 问题

如何使用 CORS 代理避免No Access-Control-Allow-Origin header"问题

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

如果您不控制前端代码向其发送请求的服务器,那么该服务器的响应问题只是缺少必要的Access-Control-Allow-Origin标头,您仍然可以通过 CORS 代理发出请求.

If you don’t control the server your frontend 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.

您可以使用来自 https://github.com/的代码轻松运行您自己的代理Rob--W/cors-anywhere/.
您还可以在 2-3 分钟内轻松将自己的代理部署到 Heroku,只需 5 个命令:

You can easily run your own proxy with code from https://github.com/Rob--W/cors-anywhere/.
You can also easily deploy your own proxy to Heroku in 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

运行这些命令后,您最终将拥有自己的 CORS Anywhere 服务器,例如,https://cryptic-headland-94862.herokuapp.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/.

现在,为您的请求 URL 添加代理 URL 的前缀:

Now, prefix your request URL with the URL for your proxy:

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

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

Adding the proxy URL as a prefix causes the request to get made through your proxy, which:

  1. 将请求转发到 https://example.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.

即使请求触发浏览器执行 CORS 预检 OPTIONS 请求,这也有效,因为在这种情况下,代理还会发送 Access-Control-Allow-Headers<使预检成功所需的/code> 和 Access-Control-Allow-Methods 标头.

This works even if the request is one that triggers browsers to do a CORS preflight OPTIONS request, because in that case, the proxy also sends the Access-Control-Allow-Headers and Access-Control-Allow-Methods headers needed to make the preflight succeed.

如何避免 CORS 预检

问题中的代码会触发 CORS 预检——因为它发送了一个 Authorization 标头.

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

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

即使没有,Content-Type: application/json 标头也会触发预检.

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

什么是预检":在浏览器尝试问题代码中的POST之前,它首先向服务器发送一个OPTIONS请求,以确定是否服务器选择接收具有 AuthorizationContent-Type: application/json 标头的跨源 POST.

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

它与一个小的 curl 脚本配合得很好 - 我得到了我的数据.

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

要正确使用 curl 进行测试,您必须模拟浏览器发送的预检 OPTIONS:

To properly test with curl, you must emulate the preflight OPTIONS 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 from that OPTIONS request must have 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

如果 OPTIONS 响应不包含这些标头,浏览器将在那里停止并且永远不会尝试发送 POST 请求.此外,响应的 HTTP 状态代码必须是 2xx——通常是 200 或 204.如果是任何其他状态代码,浏览器将在那里停止.

If the OPTIONS response doesn’t include those headers, the browser will stop right there and never 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 请求的支持.在这种情况下,其他服务器通常以 405不允许方法"状态代码响应.

The server in the question responds 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.

因此,如果服务器使用 405 或 501 响应 OPTIONS 请求,您将永远无法从前端 JavaScript 代码直接向该服务器发出 POST 请求或者 200 或 204 以外的任何内容,或者如果没有使用那些必要的响应标头进行响应.

So you’ll never 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:

  • 如果服务器不需要 Authorization 请求标头,而是依赖于嵌入在 POST 请求正文中的身份验证数据或作为查询参数
  • 如果服务器不需要 POST 主体具有 Content-Type: application/json 媒体类型而是接受 POST> 正文为 application/x-www-form-urlencoded,带有一个名为 json(或其他)的参数,其值为 JSON 数据
  • if the server didn’t require an Authorization request header but instead, e.g., relied on authentication data embedded in the body of the POST request or as a query param
  • 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

我收到另一条错误消息:

响应中Access-Control-Allow-Origin"标头的值当请求的凭证模式为时不能是通配符*"'包含'.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 代码访问响应.相反,这种情况下的值必须与您的前端代码的来源完全匹配,http://127.0.0.1:3000.

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

请参阅凭据请求和通配符 在 MDN HTTP 访问控制 (CORS) 文章中.

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

如果您控制要向其发送请求的服务器,则处理这种情况的常用方法是配置服务器以获取 Origin 请求标头的值,并回显/反射返回到 Access-Control-Allow-Origin 响应头的值;例如,使用 nginx:

If you control the server you’re sending the request to, 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; e.g., with nginx:

add_header Access-Control-Allow-Origin $http_origin

但这只是一个例子;其他(网络)服务器系统也有类似的方法来回显原始值.

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

我正在使用 Chrome.我也试过使用那个 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. 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-* 标头是 response 标头.你永远不想在请求中发送它们.这样做的唯一效果是触发浏览器进行预检.

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

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

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