http请求中的 OPTIONS 详解

一、导读

有过跨域请求的同学们应该发现过一个http请求有时会请求2次的时候,今天就给大家说说这个http请求的OPTIONS 方法是如何产生以及作用是啥。

二、解释

互联网上的各个节点之间本来都是连通的,但是有些节点,比如我们的个人电脑连接另外一些节点(比如服务器)的时候,总是通过浏览器。这样,浏览器作为一个中间人,就有机会管理一些连接,就好像高速路上的收费站检查进出的车辆。

这个类比还有一个可以借鉴的地方:就像收费站会根据一些章程文件来检查进出车辆,浏览器也根据一些技术约定来管理进出的连接。这些技术约定由 W3C,WHATWG 等组织制定。

我们今天要谈的跨域请求,Cross Origin Resource Sharing ( CORS ),就是浏览器执行的一种技术约定,一种 Protocol。

那么具体来说,到底什么是“跨域请求”呢?

首先,“域”(Origin)是指浏览器地址栏中显示的主机地址。“跨域”,就是网页程序想要请求的地址和地址栏中的“域”不同。合起来就是,一个网页程序想要去别人家的地址[1]索要资源。

这个概念基本上只在涉及浏览器的网页应用中会出现。在浏览器之外,“域”的概念(浏览器地址栏中显示的主机地址)都没有了,更谈不上跨域了。但这个概念也是重要的,因为目前主流的浏览器,不论是电脑上的,还是手机上的,都支持这个约定;也就是说,不论是哪家的浏览器,只要在他们的地盘活动,这个约定是必须要考虑的。

如果违反这个 Protocol 中的约定,会有什么后果么?

有一些 request 会收不到 response,因为 response 被浏览器拦截了,内容不告诉你
另外一些请求会根本发不出去,因为浏览器不允许发出那样的 request。
接下来,我们就来讨论,哪些情况会导致收不到 response,哪些情况会导致 request 失败。

Simple Requests 如何跨域请求
我们定义一种 request 叫 simple request,当且仅当一个 request 满足下面全部条件的时候:

request method 只能是 GET,HEAD,POST
只能有浏览器默认添加的 headers,以及一些 CORS 协议中默许的 headers 比如 Accept 等,更多被允许的 headers,可以看这里
Content-Type这个header的value只能是特定的3个
在 js 代码中不要使用特定的方法,可参见文档
一旦一个 request 是 simple request,那么,尽管这个请求是跨域的,它也会被浏览器直接放行。但是,在 response 返回的时候,浏览器并不会把 response 直接交给你,而是去检查这个 response 的 headers 中有没有 Access-Control-Allow-Origin,以及这个 header 的 value 包含 request 发出的地址(也就是“域”)。

扫描二维码关注公众号,回复: 6072846 查看本文章

如果两个条件都满足, response 会被返回给发出请求的程序;如果没有这个 header 或者 value 不对, response 就会被拦截下来,因为在浏览器看来,这个 response 不属于你(因为服务器没有明确允许你这个“域”来请求它)。如果你使用的是 chrome 浏览器,在 response 被拦截下来的时候,console 中会显示一个类似于下面的错误信息:

repeat this in your console
尽管发出request的程序无法得到 response,但是这个请求实际上是被发出了的,而且服务器也会完整的处理这个request。可以想见,如果被请求的服务器支持被跨域请求,那么它一定会想办法在 response 中加上Access-Controll-Allow-Origin这个 header,并且附上合适的值。什么是合适的值呢?在 Request 的headers中会有一个 Origin,只要Access-Controll-Allow-Origin包含这个 Origin 就可以了(如果是 wildcard *,那么就等于包含所有的 Origin)。

Preflighted Requests 如何跨域请求
进行跨域的请求只会被分为两类,第一类就是 Simple Requests,其他的都是 Preflighted Requests。所以,只要一个请求,不满足上面一小节中对 simple requests 的要求,就是 preflighted requests 了。也就是说,一个请求只要满足下面几个条件中的任意一个就可以被划归此类了:

不是 GET,HEAD,POST 请求;比如是 PUT 请求
包含一些非 CORS 协议默许的 headers,比如Authorization,X-Request-With 或者一些自定义的headers。
Content-Type 不是默许的那 3 种
在 js 代码中使用了特定的方法,可参见文档
看完了 preflighted requests 的入选条件,我们再来从字面和行为上理解一下这种跨域请求。preflight 的中文意思是起飞前的,而 preflighted 的意译可能是:被在起飞前搞了一番的(有点蹩脚,哈哈)。这种跨域请求的实际行为也确实包含 preflight (起飞前)的部分:在一个请求被发出之前,浏览器会先发一个 OPTIONS 请求到目标“域”的服务器上,这个提前发出的请求,被称为 preflight request。

讲完了 preflight request 这个最重要的概念,我们可以比较方便的梳理这种跨域请求的流程:

浏览器发送 preflight request(那个 OPTIONS 请求[2])
浏览器收到 preflight response(也就是刚刚那个 request 的返回)
浏览器根据 preflight response 中的 Access-Control-Allow-Origin, Access-Control-Allow-Headers以及其他Access-Control-*类的headers 中的 value 来判断网页程序真正要发出的 request 是否符合要求
如果这个 request 符合要求,request 被发出,网页程序可以收到正常的 response(如果不出网络通讯上的意外);如果这个 request 被判定为不符合要求,这个 request 干脆就不会被发出。

三、后记

一般的跨域请求有多种方式来解决,可以服务器设置跨域请求;可以使用代理的方式,等等。

猜你喜欢

转载自blog.csdn.net/qq_37054881/article/details/89707204
今日推荐