javascript - 尝试从REST API获取数据时,请求的资源上没有“Access-Control-Allow-Origin” header

标签 javascript api cors fetch-api preflight

我正在尝试从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插件,但是随后出现另一条错误消息:


  响应中“ Access-Control-Allow-Origin”标头的值
  当请求的凭据模式为时,不得为通配符'*'
  '包括'。因此,不允许来源'http://127.0.0.1:3000'
  访问。发起的请求的凭据模式
  XMLHttpRequest由withCredentials属性控制。

最佳答案

该答案涉及很多领域,因此分为三个部分:


如何使用CORS代理来解决“无访问权限-允许-来源标头”问题
如何避免CORS飞行前
如何解决“ Access-Control-Allow-Origin标头一定不能为通配符”的问题




如何使用CORS代理来解决“无访问权限-允许-来源标头”问题

如果您不控制服务器,则您的前端JavaScript代码正在向其发送请求,并且该服务器的响应问题仅在于缺少必需的Access-Control-Allow-Origin标头,您仍然可以使工作正常进行-通过CORS代理请求。为了展示它是如何工作的,首先这里是一些不使用CORS代理的代码:



const url = "https://example.com"; // site that doesn’t send Access-Control-*
fetch(url)
.then(response => response.text())
.then(contents => console.log(contents))
.catch(() => console.log("Can’t access " + url + " response. Blocked by browser?"))





catch块在那里被击中的原因是,浏览器阻止该代码访问从https://example.com返回的响应。而浏览器这样做的原因是,响应缺少Access-Control-Allow-Origin响应标头。

现在,这里是完全相同的示例,只是添加了一个CORS代理:



const proxyurl = "https://cors-anywhere.herokuapp.com/";
const url = "https://example.com"; // site that doesn’t send Access-Control-*
fetch(proxyurl + url) // https://cors-anywhere.herokuapp.com/https://example.com
.then(response => response.text())
.then(contents => console.log(contents))
.catch(() => console.log("Can’t access " + url + " response. Blocked by browser?"))





注意:如果在尝试https://cors-anywhere.herokuapp.com时关闭或不可用,请参见下文,了解如何在2-3分钟内在Heroku上部署自己的CORS Anywhere服务器。

上面的第二个代码段可以成功访问响应,因为获取请求URL并将其更改为https://cors-anywhere.herokuapp.com/https://example.com(通过在其前面加上代理URL前缀)会导致请求通过该代理发出,然后:


将请求转发到https://example.com
接收来自https://example.com的响应。
Access-Control-Allow-Origin标头添加到响应中。
将带有添加的标头的响应传递回请求的前端代码。


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

您可以使用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加上https://cors-anywhere.herokuapp.com前缀,不如为您自己的实例的URL前缀;例如https://cryptic-headland-94862.herokuapp.com/https://example.com

因此,如果您尝试使用https://cors-anywhere.herokuapp.com时发现它已关闭(有时会关闭),然后考虑获取一个Heroku帐户(如果您尚未使用)并拿2或花费3分钟完成上述步骤,以在Heroku上部署您自己的CORS Anywhere服务器。

无论您是运行自己的服务器还是使用https://cors-anywhere.herokuapp.com或其他开放式代理,即使该请求是触发浏览器执行CORS预检OPTIONS请求的请求,该解决方案也将起作用—因为在这种情况下,代理还会发送回使预检成功所需的Access-Control-Allow-HeadersAccess-Control-Allow-Methods标头。



如何避免CORS飞行前

问题中的代码会触发CORS预检-因为它发送了Authorization标头。

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

即使没有,Content-Type: application/json标头也将触发预检。

“预检”的含义是:在浏览器尝试问题代码中的POST之前,它将首先向服务器发送OPTIONS请求-确定服务器是否选择接收跨域包含POSTAuthorization标头的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)。如果是其他任何状态代码,浏览器将在那里停止。

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

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

避免触发该案件的预检的方法是:


如果服务器不需要Authorization请求标头,而是(例如)依赖于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)文章中的Credentialed requests and wildcards

如果您控制要向其发送请求的服务器,则处理这种情况的一种常用方法是将服务器配置为采用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-*标头是响应标头。您永远不想在请求中发送它们。唯一的影响就是触发浏览器进行预检。

关于javascript - 尝试从REST API获取数据时,请求的资源上没有“Access-Control-Allow-Origin” header ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48046637/

相关文章:

javascript - 将字符串列表转换为带有 mustache 的 html 列表

javascript - Mongodb:如何从数据库中获取第一个文档然后将其删除

javascript - 使用javascript重定向django url

cocoa - Delphi 有哪些好的 Cocoa API 端口

javascript - Yammer JS SDK——CORS 问题

javascript - Ace Editor - 多词高亮规则

api - 如何在 unix 中通过 curl 进行 Twitter API 调用

javascript - 发送ajax请求到远程node.js服务器

node.js - nginx 反向代理设置在执行 CORS 请求时不保留 session ID

java - 与币安不同的 EMA(指数移动平均线)