我正在尝试从 HP Alm 的 REST API 中获取一些数据。它适用于一个小的 curl 脚本——我得到了我的数据。
现在使用 JavaScript、fetch 和 ES6(或多或少)似乎是一个更大的问题。我不断收到此错误消息:
Fetch API 无法加载。对预检请求的响应不
通过访问控制检查:没有“Access-Control-Allow-Origin”标头
出现在请求的资源上。来源 ‘ [ [ [http://127.0.0.1:3000’](http://127.0.0.1:3000’](http://127.0.0.1:3000’](http://127.0.0.1:3000’)](http://127.0.0.1:3000’](http://127.0.0.1:3000’](http://127.0.0.1:3000’](http://127.0.0.1:3000’))) 是
因此不允许访问。响应具有 HTTP 状态代码 501。
如果不透明的响应满足您的需求,请将请求的模式设置为
‘no-cors’ 来获取禁用 CORS 的资源。
我知道这是因为我试图从我的本地主机中获取该数据,并且解决方案应该使用 Cross-Origin Resource Sharing (CORS) 。我以为我确实做到了,但不知何故,它要么忽略了我在标题中写的内容,要么问题出在其他问题上。
那么,是否存在实施问题?我做错了吗?不幸的是,我无法检查服务器日志。我真的有点卡在这里。
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 插件,但随后我收到另一条错误消息:
响应中“Access-Control-Allow-Origin”标头的值
当请求的凭证模式为
‘包括’。因此不允许使用来源“ http://127.0.0.1:3000”
使用权。发起请求的凭证模式
XMLHttpRequest 由 withCredentials 属性控制。
原文由 daniel.lozynski 发布,翻译遵循 CC BY-SA 4.0 许可协议
这个答案涵盖了很多领域,因此分为三个部分:
如何使用 CORS 代理来避免 “No Access-Control-Allow-Origin 标头” 问题
如何避免 CORS 预检
如何解决 “Access-Control-Allow-Origin 标头不能是通配符” 问题
如何使用 CORS 代理来避免 “No Access-Control-Allow-Origin 标头” 问题
如果您不控制前端代码发送请求的服务器,而该服务器的响应问题只是缺少必要的
Access-Control-Allow-Origin
标头,您仍然可以让事情正常工作-通过 CORS 代理发出请求。您可以使用 https://github.com/Rob–W/cors-anywhere/ 中的代码轻松运行自己的代理。
您还可以在 2-3 分钟内轻松地将自己的代理部署到 Heroku,只需 5 个命令:
运行这些命令后,您将拥有自己的 CORS Anywhere 服务器,例如
https://cryptic-headland-94862.herokuapp.com/
。现在,在您的请求 URL 前加上代理的 URL:
添加代理 URL 作为前缀会导致请求通过您的代理发出,其中:
将请求转发到
https://example.com
。接收来自
https://example.com
的响应。将
Access-Control-Allow-Origin
标头添加到响应中。将该响应连同添加的标头一起传递回请求的前端代码。
然后浏览器允许前端代码访问响应,因为带有
Access-Control-Allow-Origin
响应标头的响应是浏览器看到的。即使请求是触发浏览器执行 CORS 预检
OPTIONS
请求的请求,这也有效,因为在这种情况下,代理还会发送进行预检所需的Access-Control-Allow-Headers
和Access-Control-Allow-Methods
标头成功。如何避免 CORS 预检
问题中的代码触发 CORS 预检——因为它发送了
Authorization
标头。https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Preflighted_requests
即使没有,
Content-Type: application/json
标头也会触发预检。“预检”是什么意思:在浏览器尝试问题代码中的
POST
之前,它首先向服务器发送一个OPTIONS
请求,以确定服务器是否选择接收具有Authorization
和Content-Type: application/json
的跨域POST
Content-Type: application/json
标头。要使用
curl
正确测试,您必须模拟浏览器发送的预检OPTIONS
:…将
https://the.sign_in.url
sign_in
为您的实际登录 URL。浏览器需要来自该
OPTIONS
请求的响应必须具有如下标头:如果
OPTIONS
响应不包含这些标头,则浏览器将停在那里并且永远不会尝试发送POST
请求。此外,响应的 HTTP 状态码必须是 2xx——通常是 200 或 204。如果是任何其他状态码,浏览器就会停在那里。问题中的服务器使用 501 状态代码响应
OPTIONS
请求,这显然意味着它试图表明它不实现对OPTIONS
请求的支持。在这种情况下,其他服务器通常会以 405“方法不允许”状态代码进行响应。因此,如果服务器使用 405 或 501 或 200 或 204 以外的任何内容响应该
OPTIONS
请求,或者如果没有响应那些必要的请求,您将永远无法从前端 JavaScript 代码直接向该服务器发出POST
请求响应头。避免为问题中的案例触发预检的方法是:
如果服务器不需要
Authorization
请求标头,而是依赖于嵌入在POST
请求正文中的身份验证数据或作为查询参数如果服务器不要求
POST
正文具有Content-Type: application/json
媒体类型,而是接受POST
正文为application/x-www-form-urlencoded
并带有名为json
(或其他)的参数,其值为JSON数据如何解决 “Access-Control-Allow-Origin 标头不能是通配符” 问题
对于具有凭据的请求,如果
Access-Control-Allow-Origin
标头的值为*
,浏览器将不会让您的前端 JavaScript 代码访问响应。相反,这种情况下的值必须与前端代码的来源http://127.0.0.1:3000
完全匹配。请参阅 MDN HTTP 访问控制 (CORS) 文章中的 凭据请求和通配符。
如果您控制要向其发送请求的服务器,处理这种情况的常用方法是将服务器配置为获取
Origin
请求标头的值,并将其回显/反射回Access-Control-Allow-Origin
的值Access-Control-Allow-Origin
响应头;例如,使用 nginx:但这只是一个例子;其他(网络)服务器系统也有类似的方式来回显原始值。
Chrome CORS 插件显然只是简单地将
Access-Control-Allow-Origin: *
标头注入到浏览器看到的响应中。如果插件更智能,它会做的是将那个虚假Access-Control-Allow-Origin
响应标头的值设置为前端 JavaScript 代码的实际来源http://127.0.0.1:3000
。所以避免使用那个插件,即使是为了测试。这只是一种分心。要在没有浏览器过滤的情况下测试从服务器获得的响应,最好使用上面的
curl -H
。至于问题中
fetch(…)
请求的前端 JavaScript 代码:删除这些行。
Access-Control-Allow-*
标头是 响应 标头。你永远不想在请求中发送它们。这样做的唯一效果是触发浏览器进行预检。