我正在尝试从 HP Alm 的 REST API 中获取一些数据。它与一个小的 curl 脚本一起使用时效果很好 - 我得到了我的数据。
现在使用 JavaScript 进行此操作,获取和 ES6(或多或少)似乎是一个更大的问题。我不断收到此错误消息:
提取 API 无法加载。对预检请求的响应未通过访问控制检查:在所请求的资源上不存在 “Access-Control-Allow-Origin” 标头。因此,不允许访问源 “ http://127.0.0.1:3000”。响应的 HTTP 状态码为 501。如果不透明的响应满足您的需求,请将请求的模式设置为 “no-cors”,以在禁用 CORS 的情况下获取资源。
我了解这是因为我试图从本地主机中获取数据,并且解决方案应使用 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 插件,但是随后出现另一条错误消息:
当请求的凭据模式为 “include” 时,响应中 “Access-Control-Allow-Origin” 标头的值不得为通配符 “*”。因此,不允许访问源 “ http://127.0.0.1:3000”。 XMLHttpRequest 发起的请求的凭据模式由 withCredentials 属性控制。
该答案涉及很多领域,因此分为三个部分:
如何使用 CORS 代理来避免“无访问控制 - 允许 - 源标头”问题
如果您不控制服务器,您的前端代码正在向其发送请求,并且该服务器的响应问题仅在于缺少必要的Access-Control-Allow-Origin
标头,那么您仍然可以使工作正常进行 - 通过 CORS 代理发出请求。
您可以使用 https://github.com/Rob--W/cors-anywhere / 中的代码轻松运行自己的代理。
您还可以使用以下 5 个命令在 2-3 分钟内轻松地将自己的代理部署到 Heroku:
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
请求 - 确定服务器是否选择接收具有Authorization
POST
和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
请求,这显然意味着它试图表明它未实现对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 标头一定不能为通配符” 的问题
我收到另一条错误消息:
当请求的凭据模式为 “include” 时,响应中 “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
但这只是一个例子。其他(网络)服务器系统提供了类似的方法来回显原始值。
我正在使用 Chrome。我也尝试使用该 Chrome CORS 插件
Chrome CORS 插件显然只是简单地将Access-Control-Allow-Origin: *
标头注入浏览器看到的响应中。如果插件更聪明,那么它将把该伪造的Access-Control-Allow-Origin
响应标头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-*
标头是响应标头。您永远都不想在请求中发送它们。唯一的效果就是触发浏览器进行预检。
当客户端 URL 和服务器 URL 不匹配(包括端口号)时,将发生此错误。在这种情况下,您需要为跨源资源共享的 CORS 启用服务。
如果您托管的是 Spring REST 服务,则可以在博客文章 “ Spring Framework 中的 CORS 支持” 中找到它。
如果您正在使用 Node.js 服务器托管服务,则
npm install cors --save
将以下行添加到您的 server.js
var cors = require('cors')
app.use(cors()) // Use this after the variable declaration
出现问题是因为您在前端中添加了以下代码作为请求标头:
headers.append('Access-Control-Allow-Origin', 'http://localhost:3000');
headers.append('Access-Control-Allow-Credentials', 'true');
这些标头属于response ,而不是 request。因此,删除它们,包括以下行:
headers.append('GET', 'POST', 'OPTIONS');
您的请求具有'Content-Type: application/json'
,因此触发了所谓的 CORS 预检。这导致浏览器使用 OPTIONS 方法发送了请求。有关详细信息,请参见CORS 飞行前检查。
因此,在后端,您必须通过返回包括以下内容的响应标头来处理此预检请求:
Access-Control-Allow-Origin : http://localhost:3000
Access-Control-Allow-Credentials : true
Access-Control-Allow-Methods : GET, POST, OPTIONS
Access-Control-Allow-Headers : Origin, Content-Type, Accept
当然,实际语法取决于您在后端使用的编程语言。
在您的前端,应该像这样:
function performSignIn() {
let headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('Accept', 'application/json');
headers.append('Authorization', 'Basic ' + base64.encode(username + ":" + password));
headers.append('Origin','http://localhost:3000');
fetch(sign_in, {
mode: 'cors',
credentials: 'include',
method: 'POST',
headers: headers
})
.then(response => response.json())
.then(json => console.log(json))
.catch(error => console.log('Authorization failed : ' + error.message));
}