CORS: 为什么我的浏览器不会发送OPTIONS预检请求?

35

根据我阅读的关于CORS的内容,我理解它应该按照以下方式工作:

  1. 客户端上的脚本尝试从具有不同来源的服务器获取资源
  2. 浏览器拦截此请求并首先向相同的URL发送预检 OPTIONS 请求
  3. 如果对此预检请求的响应包含适当的标头(例如,Access-Control-Allow-Origin: *),则浏览器会理解可以发送主请求并进行发送。
  4. 响应返回到客户端脚本。

我设置了一个如下测试:

  • 使用Go编写的服务器接受GET和OPTIONS请求(使用CURL进行检查),并在响应中设置Access-Control-*标头。
  • 所在端口由另一台服务器提供的简单HTML页面,其中包含以下脚本($代表jQuery):

    $.ajax({
      type: "GET",
      crossDomain: true,
      url: "http://local.site.com/endpoint,
      success: function (data) {
        alert(data);
      },
      error: function (request, error) {
        alert(error);
      }
    });
    

但是,当我调用该方法时,在Chrome 49和Firefox 33中的网络选项卡中只看到一个GET请求,没有预检OPTIONS请求。

以下是来自Chrome的GET请求的详细信息:

Accept:*/*
Accept-Encoding:gzip, deflate, sdch
Accept-Language:en-US,en;q=0.8,ru;q=0.6
Connection:keep-alive
Host:local.adform.com
Origin:http://localhost:7500
Referer:http://localhost:7500/test-page.html
User-Agent:Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36

和相应的响应:

Access-Control-Allow-Headers:Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization
Access-Control-Allow-Methods:POST, GET, OPTIONS, PUT, DELETE
Access-Control-Allow-Origin:*
Content-Length:2
Content-Type:text/plain; charset=utf-8
Date:Wed, 03 Aug 2016 10:53:19 GMT

你们有什么想法,为什么我的浏览器不会发送预检请求


2
预检请求并不总是执行,如果使用GET方法,则可能根本不需要执行(或者很少需要执行)- 请参阅文档以了解何时需要执行预检请求。 - Jaromanda X
1
更多关于浏览器何时发出预检请求的信息,请参见此处 - robertklep
@JaromandaX:谢谢你的评论。如果不是总是需要预检请求,那么克服“同源策略”的(常规)方法是什么呢?场景是这样的:我们创建一个脚本,将被嵌入到第三方网站中,并向我们发送_我们的_ cookies。据我了解,如果没有预检请求,浏览器不会将cookies发送到我们的服务器,对吗? - ffriend
请查看我链接的文档中的下一节 - 带凭据的请求 - Jaromanda X
我建议阅读整个页面 - 它很有信息量。 - Jaromanda X
@JaromandaX:感谢建议,那是一篇有趣的阅读。我最终成功了,并将发布该方法作为答案。 - ffriend
1个回答

19

正如评论员所指出的,使用GET时,浏览器并不总是发送预检请求OPTIONS。如果确实需要预检,一种让浏览器发送预检请求的方法是设置自定义标题(例如 "X-PINGOVER: pingpong" 或其他)。请注意,服务器还应该通过将该请求头添加到 "Access-Control-Allow-Headers" 响应头来允许此请求头。


我的根本目标是将具有域名 a.com 的 cookies 传递给来自另一个站点(s) b.com 的服务器(这是在第三方网站上跟踪用户时的常见用例)。结果发现,要随请求发送cookie需要做更多的工作。

客户端(即JavaScript中),需要启用跨域请求并允许传递凭据。例如,以下带有jQuery的请求对我有用:

$.ajax({
  type: "GET",
  url: "http://example.com",
  xhrFields: {
    withCredentials: true           // allow passing cookies
  },
  crossDomain: true,                // force corss-domain request                
  success: function (data) { ... },
  error: function (request, error) { ... }
});

服务器端,需要设置2个响应头:

  • Access-Control-Allow-Credentials: true
  • Access-Control-Allow-Origin: <请求者来源>

其中<请求者来源>是发起调用的网站的协议+主机名+端口。请注意,通配符*在许多浏览器中可能无法正常工作,因此服务器解析请求的Referer头并响应特定允许的来源可能更加合理。


请参阅MDN Preflight Request文档以获取更多信息。 - styfle
需要注意的是,您的服务器可能不会将“X-PINGOVER”视为标头并返回错误。为解决此问题,在服务器代码中设置Access-Control-Allow-Headers的值时,请包括X-PINGOVER - user2023861

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