不一致的 AJAX 预检选项请求

Posted

技术标签:

【中文标题】不一致的 AJAX 预检选项请求【英文标题】:Inconsistent AJAX Preflight OPTIONS Requests 【发布时间】:2016-04-10 18:17:46 【问题描述】:

我希望这个问题不会太模糊,但我觉得我缺少一些基本的东西。这是我所处的情况。我们有一个胖客户端应用程序,它在每个用户的本地计算机(Windows 7)上运行,并提供一个 javascript API 来与应用程序交互。我们有一个托管在服务器上的 Web 应用程序,但向 localhost 发出 AJAX 请求以与厚客户端 JS API 交互。

在一台机器上,我们加载网络应用程序。向 localhost 发出的所有 AJAX 请求都是在没有 OPTIONS 预检请求的情况下发出的。 JS 控制台的 net 选项卡显示正在发出的 POST,并且响应成功(我要告诉胖客户端响应中要包含哪些标头,包括所有必要的 CORS 标头)。

在另一台机器上,我们从同一台服务器加载 Web 应用程序。但是,当向 localhost 发出 AJAX 请求时,会发出(预期的)OPTIONS 请求。不幸的是,胖客户端没有响应相应的 Access-Control-Allow-Origin 标头,因此不允许进行后续 POST。

浏览器无关紧要,我们已经测试了最新版本的 Firefox 和 Chrome,并且我们总是从机器上获得相同的行为。我们已经在 4 台机器上进行了测试——2 台发送 OPTIONS,2 台只发送没有前面的 OPTIONS 的 POST。似乎 AJAX 目的地是否与来源不同以及它是否发送预检请求取决于浏览器。我终其一生都无法弄清楚为什么我的机器不发送任何 OPTIONS 请求,即使 AJAX 请求(到 localhost 或 lvh.me)与网页源的主机/域不同,但从另一台机器加载相同的页面确实会产生它们。

是否有某种浏览器设置会影响这一点?还是 Windows 设置?为什么我们会在这方面看到不同的行为?

更新1:

为了简化和澄清,我有两台机器,machineA 和 machineB。两台机器都从远程服务器 example.com 加载 Web 应用程序。两台机器都在其本地机器上安装了一个完整的厚应用程序,该应用程序提供 Web 服务器和 API。从 example.com 加载的 Web 应用程序包含用于向 localhost 提交 AJAX 请求的 JavaScript 代码,以便每个用户都可以与他们自己的胖客户端应用程序的本地实例进行交互。由于 localhost != example.com,请求应该被认为是跨域的。

当 machineA 向 localhost 提交标准的 jQuery AJAX 请求时(其中,POST 的有效负载只是“true”,这将导致厚客户端回显该值 - 仅用于轮询以确定厚客户端甚至可用),POST 是直接进行的,没有 OPTIONS 请求: POST http://localhost:4000/cgi-bin/Extensions/GeoLinkConsole/evaljs.html 200 OK 0ms 有趣的是,该 POST 的响应必须包含带有 example.com 的 Access-Control-Allow-Origin 标头,以避免 CORS 错误。

当 machineB 从 example.com 加载相同的页面时,向 localhost 提交相同的标准 jQuery AJAX 请求,具有相同的“真实”有效负载,浏览器提交一个 OPTIONS 请求。这是我实际期望的行为。带有 200 OK 的 OPTIONS 请求响应,但是它不包含 Access-Control-Allow-Origin 标头,因此它会阻止 POST 通过。这不是我主要关心的问题,显然是胖客户端的问题。我最大的问题是为什么有些机器正在生成 OPTIONS 而有些则没有。我希望这有助于解释。

更新 2:

我包含了两组请求标头。我不知道实际的请求标头会影响是否会生成 OPTIONS 请求。马上,我看到发送 OPTIONS 的那个包含另一个不包含的 Acces-Control-Request-Headers 和 Access-Control-Request_Method...

来自发送 POST 且没有 OPTIONS 的机器的请求标头:

Accept:    text/plain, */*; q=0.01
Accept-Encoding:    gzip, deflate
Accept-Language:    en-US,en;q=0.5
Content-Length:    354
Content-Type:   text/plain; charset=UTF-8
Host:    localhost:4000
Origin:    http://example.com:7802
Referer:    http://example.com:7802/
User-Agent:    Mozilla/5.0 (Windows NT 6.1; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0

来自发送 OPTIONS 的机器的请求标头:

Accept:    */*
Accept-Encoding:    gzip, deflate, sdch
Accept-Language:    en-US,en;q=0.8
Access-Control-Request-Headers:    accept, content-type, x-csrftoken
Access-Control-Request-Method:    POST
Connection:    keep-alive
Host:    localhost:4000
Origin:    http://example.com:7802
Referer:    http://example.com:7802/
User-Agent:    Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36

此外,机器有一个“通用”标头块——我假设我刚刚发布的请求标头用于原始 POST 请求,这些与预检 OPTIONS 请求有关:

Request URL:    http://localhost:4000/cgi-bin/Extensions/GeoLinkConsole/evaljs.html
Request Method:   OPTIONS
Status Code:    200 OK
Remote Address:    127.0.0.1:4000

【问题讨论】:

感谢apsillers。我发布了更新。我希望它能澄清我的问题——我知道这有点令人困惑。 @apsillers 好的——请参阅更新 2,我包含了所有标题。当所有机器都从同一服务器加载相同的网页时,我不太明白为什么只在某些机器上附加额外的标头。 仅供参考 -- "* 我不知道实际的请求标头会影响是否会生成 OPTIONS 请求*" -- 这是 only 之一 i> 可以影响是否发送预检的事物(连同 HTTP 方法)。 很高兴知道...在这种情况下阻止 OPTIONS 请求的解决方案是尝试从请求中删除 Access-Control-Request 标头,或者向它们添加不同的值?如果您在答案中做出回应,我会将其标记为正确。非常感谢您的帮助! 所以,唯一的区别是第二台机器想要在其请求中包含一个名为x-csrftoken 的非简单标头。找出为什么,你可能会解决你的问题。解决方案是 (1) 更改胖客户端以正确容忍 x-csrftoken 标头并发送回适当的 Access-Control-Allow-HeadersAccess-Control-Allow-Origin 以响应 OPTIONS,或者 (2) 阻止客户端代码尝试发送x-csrftoken。 (另外:最终代码块中的信息不是标头。它们是 HTTP 请求的非标头部分。) 【参考方案1】:

(您可能有兴趣阅读我在 How does Access-Control-Allow-Origin header work? 和 HTML5 writeup on CORS 上的回答中的“非简单”魔药,以了解为什么会发生 OPTIONS 预检请求。)

如您所见,OPTIONS 请求包含Access-Control-Request-Headers,其中包含标头名称x-csrftoken。这意味着 JavaScript 正在尝试发送一个名为 x-csrftoken 的标头。由于这不是一个简单的 CORS 标头(即,AcceptAccept-LanguageContent-Language,有时还有Content-Type),它必须首先被预检 OPTIONS 请求允许。

除了正常的Access-Control-Allow-Origin 标头外,服务器还应使用包含x-csrftoken 标头名称的Access-Control-Allow-Headers 标头响应OPTIONS 预检。完成后,浏览器将允许实际的 POST 请求通过服务器。

或者,完全删除非简单的x-csrftoken 标头,并且不需要预检。

【讨论】:

以上是关于不一致的 AJAX 预检选项请求的主要内容,如果未能解决你的问题,请参考以下文章

Ajax 不接受 CORS(预检)响应

同源策略/AJAX 请求

ajax跨域访问数据

来自 IE 的不一致的 ajax (XDR) 响应

是否对同源域的 ajax 调用进行了预检请求?

预检响应中不允许使用方法选项