Chrome浏览器是如何决定何时发送OPTIONS请求的?

21

我有一个AngularJS WebAPI应用程序。

据我所知,浏览器会自动构造OPTIONS请求。

POST http://localhost:3048/Token HTTP/1.1
Host: localhost:3048
Connection: keep-alive
Content-Length: 78
Accept: application/json, text/plain, */*
Origin: http://localhost:2757
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Referer: http://localhost:2757/Auth/login
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8

grant_type=password&username=xxx%40live.com&password=xxx

响应:

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Length: 971
Content-Type: application/json;charset=UTF-8
Expires: -1
Server: Microsoft-IIS/8.0
Access-Control-Allow-Origin: *
Set-Cookie: .AspNet.Cookies=CpvxrR1gPFNs0vP8GAmcUt0EiKuEzLS1stLl-70O93wsipJkLUZuNdwC8tZc5M0o1ifoCjvnRXKjEBk3nLRbFlbldJLydW2BWonr5JmBjRjXZyKtcc29ggAVhZlc2E-3gGDlyoZLAa5Et8zrAokl8vsSoXmHnsjrxZw0VecB_Ry98Ln84UuKdeHlwSBnfaKKJfsN-u3Rsm6MoEfBO5aAFEekhVBWytrYDx5ks-iVok3TjJgaPc5ex53kp7qrtH3izbjT7HtnrsYYtcfPtmsxbCXBkX4ssCBthIl-NsN2wObyoEqHMpFEf1E9sB86PJhTCySEJoeUJ5u3juTnPlQnHsk1UTcO0tDb39g-_BD-I4FWS5GMwxLNtmut3Ynjir0GndwqsvpEsLls1Y4Pq7UuVCTn7DMO4seb64Sy8oEYkKZYk9tU4tsJuGD2CAIhdSc-lAmTAA78J5NOx23klkiuSe_SSiiZo5uRpas_1CFHjhi1c8ItEMpgeTsvgTkxafq5EOIWKPRxEHbCE8Dv106k5GlKK5BaH6z7ESg5BHPBvY8; path=/; HttpOnly
X-SourceFiles: =?UTF-8?B?QzpcR1xhYmlsaXRlc3Qtc2VydmVyXFdlYlJvbGVcVG9rZW4=?=
X-Powered-By: ASP.NET
Date: Tue, 13 Jan 2015 04:54:55 GMT

{"access_token":"TkJ2trqT ....

现在已登录

我退出登录,只是移除了令牌然后重新登录。有些不同的事情发生了。之前它没有发送OPTIONS请求,但现在它会发送。是否有先前请求/响应导致浏览器在第二次登录时表现不同的影响?

OPTIONS http://localhost:3048/Token HTTP/1.1
Host: localhost:3048
Connection: keep-alive
Access-Control-Request-Method: POST
Origin: http://localhost:2757
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36
Access-Control-Request-Headers: accept, authorization, content-type
Accept: */*
Referer: http://localhost:2757/Auth/login
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8

响应:

HTTP/1.1 400 Bad Request
Cache-Control: no-cache
Pragma: no-cache
Content-Length: 34
Content-Type: application/json;charset=UTF-8
Expires: -1
Server: Microsoft-IIS/8.0
X-SourceFiles: =?UTF-8?B?QzpcR1xhYmlsaXRlc3Qtc2VydmVyXFdlYlJvbGVcVG9rZW4=?=
X-Powered-By: ASP.NET
Date: Tue, 13 Jan 2015 04:56:32 GMT

{"error":"unsupported_grant_type"}

如果我重置浏览器并重新加载页面,那么它会回到以前的状态,第一次不会发送OPTIONS请求,我就能够登录。

可能我需要在服务器上进行一些更改,使其处理options请求。

但是为什么我的浏览器(Chrome)第一次不发送OPTIONS呢?


1
但是只有第二个请求发送 OPTIONS 。为什么? - Dilip
@Wayne - 是的,这是一个跨域请求。但我感到困惑。为什么第一次发送时没有OPTIONS,而后续调用会发送OPTIONS呢?我的代码和调用是相同的。我正在尝试让登录代码运行,并想知道为什么它第一次可以工作,但第二次却不行。我意识到我必须解决为什么第二次不行的问题,但为什么浏览器行为不同呢? - Samantha J T Star
@Dilip - 是的,这就是我感到困惑的地方。第一次它没有发送OPTIONS。 - Samantha J T Star
你不是漏掉了第一个请求吗?在发帖之前不应该有一个请求吗? - Wayne Ellery
@SamanthaJ - 你看过https://dev59.com/rHM_5IYBdhLWcg3wt1rD#1320708了吗? - Dilip
显示剩余6条评论
3个回答

30
无论是Chrome(或任何其他浏览器),是否发送OPTIONS请求都由CORS规范精确指定:
当调用跨源请求算法时,必须按照以下步骤进行:
...
2. 如果满足以下条件,请按照简单的跨源请求算法进行操作:

3. 否则,请按照带有预检的跨源请求算法进行操作。
注意:使用简单方法的跨源请求,其中包含不是简单作者请求头,将会进行预检请求以确保资源能够处理这些头部信息。 (与使用不是简单方法的方法的请求类似。)

您的OPTIONS请求包含以下请求头:
Access-Control-Request-Headers: accept, <b>authorization</b>, content-type

这意味着您的Angular应用程序已插入非simpleAuthorization请求标头,可能作为身份验证方案的一部分。非简单的“授权请求标头”会触发OPTIONS请求,如上面的引用所示。
为了使请求成功,您的服务器应处理OPTIONS请求并做出响应:
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Headers: authorization

要了解更多关于CORS的内容,请参见https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS


那些 author-request-headers 链接并没有提供关于它的任何信息 - 我在链接的文档中根本找不到这个术语 - 我认为 CORS 已经从 W3C 规范中移除了。我仍然可以找到我认为你所指的文档,位于 https://www.w3.org/TR/2020/SPSD-cors-20200602/#author-request-headers - Steve Campbell

1
当您第一次登录时,最有可能在登录过程中的某个地方设置了Authorization HTTP头。另一方面,在用户退出登录时,您忘记删除此标头。
当您尝试再次登录时,Authorization HTTP头仍然存在。这会触发浏览器执行预检请求(请参见Rob W的解释:https://dev59.com/UV4c5IYBdhLWcg3wc6Dr#27924344)。考虑到您尝试使用密码授权类型登录,发送Authorization头是没有意义的,因为这意味着您已经获得了授权(=已登录)。您基本上要求后端将您登录,并同时告诉后端您已经获得了授权(=已登录)。
可以通过简单地在用户退出登录时删除Authorization HTTP头来解决此问题。

0

当您登录并发送POST请求之前,您还可以清除您的Headers:

delete $http.defaults.headers.common['Authorization'];


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