我正在try 从HP Alm的REST API获取一些数据.它与一个小的curl脚本配合得很好——我得到了我的数据.

现在,用JavaScript、fetch和ES6(或多或少)实现这一点似乎是一个更大的问题.我一直收到这样的错误信息:

获取API无法加载.对飞行前请求的响应不正确

我知道这是因为我正在try 从本地主机中获取数据,而解决方案应该是使用CORS.现在我以为我真的做到了,但不知何故,它要么忽略了我在标题中写的内容,要么问题出在其他地方?

那么,是否存在实施问题?我做错了吗?很遗憾,我无法查看服务器日志(log).我真的有点困在这里了.

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.我也try 过使用Chrome CORS插件,但随后我收到另一条错误消息:

响应中"Access Control Allow Origin"头的值

推荐答案

这个答案涉及很广,所以分为三个部分:

  • 如何使用CORS代理解决大约“No Access-Control-Allow-Origin header”个问题
  • 如何避免CORS飞行前
  • 如何解决“Access-Control-Allow-Origin header must not be the wildcard”个问题

How to use a CORS proxy to avoid “No Access-Control-Allow-Origin header” problems

如果您无法控制前端代码向其发送请求的服务器,而该服务器的响应问题只是缺少必要的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添加为前缀会导致通过代理发出请求,这:

  1. 将请求转发到https://example.com.
  2. 收到https://example.com的回复.
  3. Access-Control-Allow-Origin标题添加到响应中.
  4. 将带有添加的标头的响应传递回请求的前端代码.

然后浏览器允许前端代码访问响应,因为浏览器看到的是带有Access-Control-Allow-Origin响应头的响应.

即使该请求触发浏览器执行CORS印前判断OPTIONS请求,这也会起作用,因为在这种情况下,代理还会发送使印前判断成功所需的Access-Control-Allow-HeadersAccess-Control-Allow-Methods标头.


如何避免CORS飞行前

问题中的代码会触发CORS预飞行,因为它会发送Authorization个头.

https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Preflighted_requests

即使没有这一点,Content-Type: application/json头也会触发一次印前判断.

"飞行前"的意思是:在浏览器try 问题代码中的POST之前,它首先向服务器发送OPTIONS请求,以确定服务器是否 Select 接收包含AuthorizationContent-Type: application/json个头的交叉源POST.

它与一个小的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"

…用实际的sign_in URL替换https://the.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响应不包含这些标题,浏览器将立即停止,永远不会try 发送POST请求.此外,响应的HTTP状态代码必须是2xx,通常为200或204.如果是任何其他状态代码,浏览器将立即停止.

问题中的服务器用501状态代码响应OPTIONS个请求,这显然意味着它试图表明它没有实现对OPTIONS个请求的支持.在这种情况下,其他服务器通常会使用405"不允许使用方法"状态代码进行响应.

因此,如果服务器使用405或501或除200或204以外的任何值响应OPTIONS请求,或者如果没有使用这些必要的响应头响应,您将永远无法从前端JavaScript代码直接向该服务器发出POST个请求.

避免触发问题中 case 的印前判断的方法是:

  • 如果服务器不需要Authorization请求头,而是依赖嵌入POST请求主体中的身份验证数据或作为查询参数
  • 如果服务器不要求POST body具有Content-Type: application/json媒体类型,而是将POST body接受为application/x-www-form-urlencoded,并带有一个名为json(或其他名称)的参数,该参数的值是JSON数据

如何解决“Access-Control-Allow-Origin header must not be the wildcard”个问题

我收到另一条错误消息:

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完全匹配.

请参阅MDN HTTP访问控制(CORS)文章中的Credentialed requests and wildcards.

如果您控制要向其发送请求的服务器,处理这种情况的常用方法是将服务器配置为采用Origin请求标头的值,并将其 echo /反映到Access-Control-Allow-Origin响应标头的值中;例如,使用nginx:

add_header Access-Control-Allow-Origin $http_origin

但这只是一个例子;其他(Web)服务器系统也有类似的 echo 原始值的方式.


我用的是Chrome.我也try 过使用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');

go 掉那些线.Access-Control-Allow-*个报头是response个报头.您永远不会想要向他们发送请求.这样做的唯一效果是触发浏览器执行印前判断.

Javascript相关问答推荐

Flisk和JS错误:未捕获的Syntax错误:意外的令牌'<'

提交表格后保留Web表格中的收件箱值?

react/redux中的formData在expressjs中返回未定义的req.params.id

django无法解析余数:[0] from carray[0]'

Plotly热图:在矩形上zoom 后将zoom 区域居中

我在我的Java代码中遇到了问题,代码的一部分看不到先前定义的对象

无法使用单击按钮时的useState将数据从一个页面传递到另一个页面

ChartJs未呈现

Web Crypto API解密失败,RSA-OAEP

如何 for each 输入动态设置输入变更值

为什么当我更新数据库时,我的所有组件都重新呈现?

如何组合Multer上传?

MongoDB通过数字或字符串过滤列表

按特定顺序将4个数组组合在一起,按ID分组

Plotly.js栏为x轴栏添加辅助文本

在对象的嵌套数组中添加两个属性

无法向甜甜圈图表上的ChartJSImage添加可见标签

REACT-本机错误:错误类型错误:无法读取未定义的容器的属性

在执行console.log(new X())时记录一个字符串

验证Java脚本函数中的两个变量