这个答案涵盖了很多方面,所以它分为三个部分:
- 如何使用 CORS 代理解决“No Access-Control-Allow-Origin header”问题
- 如何避免 CORS 预检
- 如何解决“Access-Control-Allow-Origin 标头不能是通配符”问题
如何使用 CORS 代理避免“No Access-Control-Allow-Origin header”问题
如果您不控制前端代码向其发送请求的服务器,并且来自该服务器的响应的问题只是缺少必要的Access-Control-Allow-Origin
标头,您仍然可以通过 CORS 发出请求来使工作正常进行代理。
您可以使用来自https://github.com/Rob--W/cors-anywhere/ 的代码轻松运行您自己的代理。
您还可以在短短 2-3 分钟内轻松将自己的代理部署到 Heroku,使用 5 个命令:
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/
.
现在,使用代理的 URL 作为请求 URL 的前缀:
https://cryptic-headland-94862.herokuapp.com/https://example.com
添加代理 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
服务器发送请求,以确定服务器是否选择接收POST
具有Authorization
和Content-Type: application/json
标头的跨域。
它与一个小的 curl 脚本一起工作得很好 - 我得到了我的数据。
要正确测试curl
,您必须模拟OPTIONS
浏览器发送的预检:
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。
浏览器从该OPTIONS
请求中需要的响应必须具有如下标头:
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。如果是任何其他状态代码,浏览器将在那里停止。
问题中的服务器以OPTIONS
501 状态代码响应请求,这显然意味着它试图表明它没有实现对OPTIONS
请求的支持。在这种情况下,其他服务器通常以 405“不允许方法”状态代码响应。
因此POST
,如果服务器OPTIONS
使用 405 或 501 或 200 或 204 以外的任何内容响应该请求,或者不使用那些必要的响应标头进行响应,则您将永远无法从前端 JavaScript 代码直接向该服务器发出请求.
避免为问题中的案例触发预检的方法是:
- 如果服务器不需要
Authorization
请求标头,而是依赖于嵌入在POST
请求正文中的身份验证数据或作为查询参数
- 如果服务器不要求
POST
主体具有Content-Type: application/json
媒体类型,而是接受POST
主体作为application/x-www-form-urlencoded
参数命名json
(或其他),其值为 JSON 数据
如何解决“Access-Control-Allow-Origin 标头不能是通配符”问题
我收到另一条错误消息:
当请求的凭据模式为
“包含”http://127.0.0.1:3000
时,响应中“Access-Control-Allow-Origin”标头的值不能是通配符“* ”。因此不允许访问
源“ ” 。XMLHttpRequest发起的请求的凭证模式由 withCredentials 属性控制。
对于具有凭据的请求,如果Access-Control-Allow-Origin
标头的值为 ,则浏览器不会让您的前端 JavaScript 代码访问响应*
。相反,这种情况下的值必须与您的前端代码的来源完全匹配http://127.0.0.1:3000
。
请参阅MDN HTTP 访问控制 (CORS) 文章中的凭据请求和通配符。
如果您控制要向其发送请求的服务器,则处理这种情况的常用方法是配置服务器以获取Origin
请求标头的值,并将其回显/反映回Access-Control-Allow-Origin
响应标头的值;例如,使用 nginx:
add_header Access-Control-Allow-Origin $http_origin
但这只是一个例子;其他(网络)服务器系统有类似的方法来回显原始值。
我正在使用 Chrome。我也试过使用那个 Chrome CORS 插件
Chrome CORS 插件显然只是简单地将Access-Control-Allow-Origin: *
标头注入浏览器看到的响应中。如果插件更智能,它会做的是将假Access-Control-Allow-Origin
响应标头的值设置为前端 JavaScript 代码的实际来源,http://127.0.0.1:3000
.
所以避免使用该插件,即使是为了测试。这只是一种分心。要在没有浏览器过滤的情况下测试您从服务器获得的响应,最好使用curl -H
上述方法。
至于fetch(…)
问题中请求的前端 JavaScript 代码:
headers.append('Access-Control-Allow-Origin', 'http://localhost:3000');
headers.append('Access-Control-Allow-Credentials', 'true');
删除这些行。该Access-Control-Allow-*
头是响应头。你永远不想在请求中发送它们。唯一的效果是触发浏览器进行预检。