No #39;Access-Control-Allow-Origin#39; header is present on the requested resource—when trying to get data from a REST API(尝试从 REST API 获取数据时,请求的资源上不存在“Access-Control-Allow-Origin标头)
问题描述
我正在尝试从 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 标头"问题
- 如何避免 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:
- 将请求转发到
https://example.com
. - 接收来自
https://example.com
的响应. - 将
Access-Control-Allow-Origin
标头添加到响应中. - 将该响应连同添加的标头传递回请求的前端代码.
- Forwards the request to
https://example.com
. - Receives the response from
https://example.com
. - Adds the
Access-Control-Allow-Origin
header to the response. - 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.
preflight"是什么意思:在浏览器尝试问题代码中的POST
之前,它首先向服务器发送一个OPTIONS
请求,以确定是否服务器选择接收具有 Authorization
和 Content-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 thePOST
request or as a query param - if the server didn’t require the
POST
body to have aContent-Type: application/json
media type but instead accepted thePOST
body asapplication/x-www-form-urlencoded
with a parameter namedjson
(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.
这篇关于尝试从 REST API 获取数据时,请求的资源上不存在“Access-Control-Allow-Origin"标头的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:尝试从 REST API 获取数据时,请求的资源上不存在“Access-Control-Allow-Origin"标头
基础教程推荐
- 直接将值设置为滑块 2022-01-01
- html表格如何通过更改悬停边框来突出显示列? 2022-01-01
- 如何使用TypeScrip将固定承诺数组中的项设置为可选 2022-01-01
- 我可以在浏览器中与Babel一起使用ES模块,而不捆绑我的代码吗? 2022-01-01
- 用于 Twitter 小部件宽度的 HTML/CSS 2022-01-01
- Chart.js 在线性图表上拖动点 2022-01-01
- Vue 3 – <过渡>渲染不能动画的非元素根节点 2022-01-01
- Electron 将 Node.js 和 Chromium 上下文结合起来意味着 2022-01-01
- 自定义 XMLHttpRequest.prototype.open 2022-01-01
- 如何使用JIT在顺风css中使用布局变体? 2022-01-01