这个答案涵盖了很多领域,所以它分为三个部分:
如何使用CORS代理来避免“无访问控制-允许起源头”问题
如何避免飞行前的CORS
如何修复“Access-Control-Allow-Origin头必须不是通配符”的问题
如何使用CORS代理来避免“无访问控制-允许起源头”问题
如果您不控制前端代码发送请求的服务器,并且来自该服务器的响应的问题只是缺乏必要的Access-Control-Allow-Origin报头,那么您仍然可以通过CORS代理发出请求来使事情正常工作。
您可以使用https://github.com/Rob--W/cors-anywhere/中的代码轻松运行自己的代理。
您还可以轻松地部署自己的代理到Heroku,只需2-3分钟,只需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 preflight OPTIONS请求的请求,这也可以工作,因为在这种情况下,代理还会发送Access-Control-Allow-Headers和Access-Control-Allow-Methods报头,以使preflight成功。
如何避免飞行前的CORS
问题中的代码触发了CORS预飞行——因为它发送了一个授权报头。
https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Preflighted_requests
即使没有,Content-Type: application/json头也会触发preflight。
“preflight”的意思是:在浏览器尝试问题中的代码中的POST之前,它首先向服务器发送一个OPTIONS请求,以确定服务器是否选择接收一个具有授权和Content-Type: application/json头的跨源POST。
它用一个小的curl脚本工作得很好-我得到了我的数据。
为了正确地测试curl,你必须模拟浏览器发送的preflight 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。如果是其他状态码,浏览器就会立即停止。
问题中的服务器用501状态代码响应OPTIONS请求,这显然意味着它试图表明它不支持OPTIONS请求。在这种情况下,其他服务器通常使用405“Method not allowed”状态代码进行响应。
因此,如果服务器对OPTIONS请求的响应是405或501,而不是200或204,或者没有响应必要的响应头,那么您将永远无法从前端JavaScript代码直接向该服务器发出POST请求。
避免在问题中触发预飞行的方法是:
如果服务器不需要授权请求头,而是依赖于POST请求体中嵌入的身份验证数据或作为查询参数
如果服务器不要求POST体具有Content-Type: application/json媒体类型,而是接受POST体为application/x-www-form-urlencoded,带有一个名为json(或其他什么)的参数,其值为json数据
如何修复“Access-Control-Allow-Origin头必须不是通配符”的问题
我得到另一个错误消息:
响应中的'Access-Control-Allow-Origin'报头的值
当请求的凭据模式为时,一定不能是通配符'*'
“包括”。因此,来源“http://127.0.0.1:3000”是不允许的
访问。对象发起的请求的凭据模式
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
但这只是一个例子;其他(web)服务器系统也有类似的方法来回显原始值。
我正在使用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-*头是响应头。你绝对不想在请求中发送它们。这样做的唯一效果是触发浏览器进行预飞行。