6

我正在构建一个基于 Backbone.js 的应用程序并面临一个奇怪的问题。

在某个时候,该应用程序请求一个集合资源,并且在 Chrome(和 Safari)中我收到这样的错误:

XMLHttpRequest cannot load http://api.mydomain.net/v1/foos/00000d/bars/000014/boots Origin http://localhost:3501 is not allowed by Access-Control-Allow-Origin.

好的,我认为 CORS 问题并归咎于我的 API。然后通过 CURL 请求这个资源:

curl -i -H'Accept: application/json' -H'X-Auth-Token: pAWp5hrCmXA83GgFzgHC' -XOPTIONS 'http://api.mydomain.net/v1/foos/00000d/bars/000014/boots'
HTTP/1.1 200 OK
Status: 200
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,X-Auth-Token
Content-Length: 0

看起来不错,现在 GET:

curl -i -H'Accept: application/json' -H'X-Auth-Token: pAWp5hrCmXA83GgFzgHC' -XGET 'http://api.mydomain.net/v1/foos/00000d/bars/000014/boots'
HTTP/1.1 204 No Content
Status: 204
Cache-Control: no-cache
Content-Length: 0
Content-Type: text/plain

如果我请求包含至少一个对象的靴子集合,一切正常。我的服务器的 CORS 标头以 arr 响应,我认为完全没问题。那么为什么浏览器会报告跨源资源问题呢?

是因为text/plain我的 204 响应的内容类型吗?

开发工具中的预检 (OPTIONS) 请求: 选项

中止响应的请求标头: 得到

4

1 回答 1

12

您还必须Access-Control-Allow-Origin在第二个请求的响应标头中包含 。这不是客户端问题,而是后端问题。

此行为符合CORS 规范,适用于以下说明(第 7.1.5 节“带预检的跨域请求”):

  1. 预检请求(细节省略)
  2. “将跨域请求状态设置为预检完成。”
  3. “这是实际请求。(...)在提出请求时请遵守以下请求规则。”
    • 如果响应的 HTTP 状态代码为 301、302、303 或 307 不适用
    • 如果最终用户取消请求 不适用
    • 如果出现网络错误 不适用
    • 否则
      执行资源共享检查。如果返回失败,请应用缓存和网络错误步骤。

您的请求在资源共享检查的第一步已经失败:

  1. 如果响应包含零个或多个Access-Control-Allow-Origin标头值,则返回失败并终止此算法。

我提供了一个简单的 NodeJS 示例来说明您的问题。
您当前的后端行为如下:

require('http').createServer(function(request, response) {
    if (request.method == 'OPTIONS') { // Handle preflight
        response.writeHead(200, {
           "Access-Control-Allow-Origin": "*",
           "Access-Control-Allow-Headers": "X-Foo"
        });
    } else {                           // Handle actual requests
        response.writeHead(204, {
          //"Access-Control-Allow-Origin": "*"
        });
    }
    response.end();
}).listen(12345);

现在,发出请求并体验失败

var x = new XMLHttpRequest;
x.open('GET', 'http://localhost:12345');
x.setRequestHeader('X-Foo','header to trigger preflight');
x.send();

回到我提供的代码,并Access-Control-Allow-Origin在响应中启用标头,然后再次测试。您的请求现在将成功。

于 2012-11-10T11:30:23.433 回答