Chrome在HTTP 302重定向时取消了CORS XHR请求

55
根据CORS规范,GET和POST请求应该透明地遵循302重定向。但是Chrome取消了我的请求。
以下是执行请求的JS代码:
var r = new XMLHttpRequest();
r.open('GET', 'https://dev.mysite.com/rest', true);
r.send();

这里是应该发生的事情:
  1. 客户端: XHR POST请求到/rest
  2. 服务器: 响应HTTP 302重定向到/rest/
  3. 客户端: 跟随重定向
但在第二步后,Chrome取消了请求。如果没有HTTP 302,请求会完美工作。我已经确认了这一点。
当请求运行时,我可以在Chrome的网络面板中看到只有一个XHR——一个被取消的POST请求,没有响应头或响应体。
使用Chrome的net-internals工具进行调试,我看到服务器发送了一个响应,之后请求被取消。以下是请求的输出:
79295: URL_REQUEST
https://dev.mysite.com/rest
Start Time: 2013-08-30 12:41:11.637

t=1377880871637 [st=    0] +REQUEST_ALIVE  [dt=13455]
t=1377880871638 [st=    1]    URL_REQUEST_BLOCKED_ON_DELEGATE  [dt=1]
                              --> delegate = "extension Adblock Plus"
t=1377880871639 [st=    2]   +URL_REQUEST_START_JOB  [dt=13453]
                              --> load_flags = 143540480 (DO_NOT_SAVE_COOKIES | DO_NOT_SEND_AUTH_DATA | DO_NOT_SEND_COOKIES | ENABLE_LOAD_TIMING | MAYBE_USER_GESTURE | REPORT_RAW_HEADERS | VERIFY_EV_CERT)
                              --> method = "POST"
                              --> priority = 2
                              --> upload_id = "0"
                              --> url = "https://dev.mysite.com/rest"
t=1377880871639 [st=    2]      HTTP_CACHE_GET_BACKEND  [dt=0]
t=1377880871639 [st=    2]     +HTTP_STREAM_REQUEST  [dt=7]
t=1377880871646 [st=    9]        HTTP_STREAM_REQUEST_BOUND_TO_JOB
                                  --> source_dependency = 79296 (HTTP_STREAM_JOB)
t=1377880871646 [st=    9]     -HTTP_STREAM_REQUEST
t=1377880871646 [st=    9]     +HTTP_TRANSACTION_SEND_REQUEST  [dt=0]
t=1377880871646 [st=    9]        HTTP_TRANSACTION_SEND_REQUEST_HEADERS
                                  --> GET /facultyportfolio-rest HTTP/1.1
                                      Host: dev.liberty.edu
                                      Connection: keep-alive
                                      Content-Length: 46
                                      Origin: http://localhost:8080
                                      User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.62 Safari/537.36
                                      Content-Type: application/json; charset=UTF-8
                                      Accept: */*
                                      Referer: http://localhost:8080/ajaxtest.html
                                      Accept-Encoding: gzip,deflate,sdch
                                      Accept-Language: en-US,en;q=0.8
t=1377880871646 [st=    9]        HTTP_TRANSACTION_SEND_REQUEST_BODY
                                  --> did_merge = true
                                  --> is_chunked = false
                                  --> length = 46
t=1377880871646 [st=    9]     -HTTP_TRANSACTION_SEND_REQUEST
t=1377880871646 [st=    9]     +HTTP_TRANSACTION_READ_HEADERS  [dt=1001]
t=1377880871646 [st=    9]        HTTP_STREAM_PARSER_READ_HEADERS  [dt=1000]
t=1377880872646 [st= 1009]        HTTP_TRANSACTION_READ_RESPONSE_HEADERS
                                  --> HTTP/1.1 302 Found
                                      Date: Fri, 30 Aug 2013 16:41:11 GMT
                                      Server: Apache/2
                                      Access-Control-Allow-Origin: http://localhost:8080
                                      Access-Control-Allow-Credentials: true
                                      Location: https://dev.mysite.com/rest/
                                      Content-Language: en-US
                                      Vary: Accept-Encoding,User-Agent
                                      Content-Encoding: gzip
                                      Content-Length: 20
                                      Connection: close
                                      Content-Type: text/plain; charset=UTF-8
t=1377880872647 [st= 1010]     -HTTP_TRANSACTION_READ_HEADERS
t=1377880872647 [st= 1010]     +URL_REQUEST_BLOCKED_ON_DELEGATE  [dt=12445]
t=1377880885091 [st=13454]        CANCELLED
t=1377880885092 [st=13455]   -URL_REQUEST_START_JOB
                              --> net_error = -3 (ERR_ABORTED)
t=1377880885092 [st=13455] -REQUEST_ALIVE

在最后,你会看到"Cancelled",因为出现了"URL_REQUEST_BLOCKED_ON_DELEGATE"。我不知道这是什么意思。但是,如果没有HTTP 302重定向,就不会出现这个错误。
有人知道是什么导致Chrome取消这个请求吗?
4个回答

33
这里的答案混杂着一些代码设置等提示,可以解决CORS的重定向问题,但是CORS规范明确指出这种CORS重定向什么情况下会失败/成功:
根据规范,浏览器应该
  1. 允许3XX重定向,如果重定向资源的请求不需要预检(例如没有自定义标头的简单CORS请求)。参见:https://www.w3.org/TR/cors/#simple-cross-origin-request-0

如果手动重定向标志未设置且响应具有301、302、303、307或308的HTTP状态码 应用重定向步骤。

  1. 不允许3XX重定向,如果重定向后的资源请求需要预检。参见:https://www.w3.org/TR/cors/#cross-origin-request-with-preflight-0

如果响应具有301、302、303、307或308的HTTP状态码 应用缓存和网络错误步骤。

我已在github repo中探索了各种CORS场景:https://github.com/monmohan/cors-experiment
这个特定的失败重定向问题也可以在此处轻松复现: https://github.com/monmohan/cors-experiment/tree/master/issue

1
你知道为什么需要预检的请求不允许30x吗?我很想了解规范中这部分的理论依据。 - mgol
显然,允许重定向是规范中的“最近更改”(https://dev59.com/PVkR5IYBdhLWcg3w9Rut),仅在2016年8月4日,因此需要一段时间才能使浏览器符合规范... - rogerdpack

15
我发现这篇关于在302响应中设置正确的Access-Control-Allow-Origin CORS头部的帖子对我的类似情况很有帮助。
调查问题显示,他的XHR没有直接着陆在启用CORS的URL上,而是通过HTTP 302(重定向)响应被重定向到它。
因此请记住,重定向URL也必须包括Access-Control-Allow-Origin头部,否则浏览器将停止尝试跨域请求。
我还发现,设置除Access-Control-Allow-Origin之外的其他CORS头部通常会导致交易取消。

1
我的请求确实收到了302,但响应包含了正确的“Access-Control-Allow-Origin”头。您说额外的响应头可能会导致取消?您能否再解释一下,或者给出哪些头会导致取消的示例? - Matthias

11

http://httpstatus.es/302

如果在除了GET或HEAD请求之外的请求中收到302状态码,则用户代理程序不得自动重定向该请求,除非可以经过用户确认,因为这可能会改变发出请求的条件。


那似乎表示 GET 请求应该重定向。我的请求是 GET,但它没有重定向。 - Matthias
2
你说的第一步是:“客户端:XHR POST请求到/rest”。 - idbehold
2
哦,抱歉,是的,最初出现问题的是POST,但也出现在GET中。也许切换到303或307 HTTP代码可能会起作用... - Matthias
2
请参考https://dev59.com/PVkR5IYBdhLWcg3w9Rut以获得更清晰的解释和规范更新。 - jprosevear

3

我曾经遇到Chrome不能在CORS请求中跟随重定向的问题。对我来说,问题是我使用的JS框架(Sencha Touch)添加了一个请求头:X-Requested-With:“XMLHttpRequest”。

一旦我删除了它(在Sencha Touch中通过调用Ext.Ajax.setUseDefaultXhrHeader(false);),它就像魅力般地工作了。

不确定为什么,但希望这些信息能帮助到某个人。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接