AJAX中出现OPTIONS请求

背景

之前有一个项目使用Zepto来发送ajax请求, 查看Nginx日志或使用Chrome Dev Tools查看请求发送情况时, 会看到每次调后台API的请求之前, 都会发送一个OPTIONS请求, 无论API要求请求的方法是GET或POST.

为什么会发送这个OPTIONS请求? 可以去掉吗?

过程

首先, 我在js代码里是没写要发送OPTIONS请求的, 后台API要的请求方法不是GET就是POST, 我只发送过这两种类型的请求.

那么, 我就以为是Zepto搞的鬼, 于是去看它的源码. Zepto的源码模块化做得很好, 直接看里面的ajax.js文件就好了. 结果, 我发现一切正常啊, 并没有这种逻辑: 发送GET/POST请求之前, 先发送一个OPTIONS请求.

不是Zepto的问题, 那我只好使用搜索大法了.

stackoverflow.com一搜, 找到了一个相似的问题, 再根据回答找到MDN的文档, 这才知道前因后果.

解惑

众所周知, ajax请求是由XMLHttpRequest对象实现的(IE10以下的浏览器不是), 而XMLHttpRequest会遵守同源策略(same-origin policy). 也即脚本只能访问相同协议/相同主机名/相同端口的资源, 如果要突破这个限制, 那就是所谓的跨域, 此时需要遵守CORS(Cross-Origin Resource Sharing)机制

那么, 允许跨域, 不就是服务端(例如Nginx)设置Access-Control-Allow-Origin: *就可以了吗? 普通的请求才是这样子的, 除此之外, 还一种叫请求叫preflighted request

preflighted request在发送真正的请求前, 会先发送一个方法为OPTIONS的预请求(preflight request), 用于试探服务端是否能接受真正的请求.

那么, 什么情况下请求会变成preflighted request呢? 文档中指出的是:

  1. 请求方法不是GET/HEAD/POST
  2. POST请求的Content-Type并非application/x-www-form-urlencoded, multipart/form-data, 或text/plain
  3. 请求设置了自定义的header字段

举个例子, 如果POST请求要传输的数据为 XML文档, Content-Typeapplication/xmltext/xml, 则发送这个请求前会发送一个预请求

有了上面的知识点, 再去看项目中ajax调用

1
2
3
4
5
6
7
8
9
10
$.ajax({
type: option.type,
url: option.url, // 具体url就不写了, 这里一定是跨域的
headers: {
openId: user.openId
},
success: function(data) {
// 省略代码...
}
})

可以看出, 跨域请求中设置了自定义的header字段, 所以该请求是preflighted request, 则请求前一定会发送一个OPTIONS作为预请求.

所以说, 在项目中ajax对后台API的调用, OPTIONS请求是不可以去掉的, 除非后台接口不再需要在请求header中设置openId

哈哈, 明白了这个道理就好办啦, 后台同学再让我把这个请求去掉, 我就说”臣妾做不到啊”

参考资料

  1. http://stackoverflow.com/questions/1256593/why-am-i-getting-an-options-request-instead-of-a-get-request
  2. https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
Fork me on GitHub