Allow
见过吗?
或许你见过,又或许没有。不过没关系,咱今天就讲它。
算了,再多展示点:这见过了吧,如果你还说没见过,那这篇文章你可真得看看…
Allow: GET, POST, HEAD
复制代码
你应该知道 http header。可以称之为http首部,或http头。暂且叫它http首部吧。
http首部是有分类的。 如下所示:
- 通用首部字段(就是无论请求方还是响应方都能使用并识别的)
- 请求首部字段(只能请求(方)时使用的)
- 响应首部字段(只能响应时使用的)
- 实体首部字段(用来补充描述“请求或响应时数据的”)
- 扩展首部字段(规范中没有定义的,可以理解为自定义首部字段)
说完首部字段的分类,那么想想Allow
在哪个分类里呢?
嗒
嗒
嗒
好了,嗒了三秒还没想到的话,那就不要想了,让我讲:
RFC 7231, section 7.4.1: Allow
如果打不开链接,那我贴出来
7.4.1. Allow
The "Allow" header field lists the set of methods advertised as
supported by the target resource. The purpose of this field is
strictly to inform the recipient of valid request methods associated
with the resource.
Allow = #method
Example of use:
Allow: GET, HEAD, PUT
The actual set of allowed methods is defined by the origin server at
the time of each request. An origin server MUST generate an Allow
field in a 405 (Method Not Allowed) response and MAY do so in any
other response. An empty Allow field value indicates that the
resource allows no methods, which might occur in a 405 response if
the resource has been temporarily disabled by configuration.
A proxy MUST NOT modify the Allow header field -- it does not need to
understand all of the indicated methods in order to handle them
according to the generic message handling rules.
复制代码
从rfc7232规范中来看,它应该是个响应首部字段
那 以上知道Allow
是个响应首部字段
后,那该想一想,那啥时候会响应这个首部字段呢???
我猜聪明的宁一定想到了,当然 如果没想到也没关系,那肯定是我指引的不到胃,而不是聪明的你不够聪明!
想一想,在你的工作中,发送GET
请求和POST
请求是不是游仞有余、顺手拈来,甚至熟练的你闭着眼睛都能写了吧。想必是
那么在你不经意间冒了一个OPTIONS
请求你是不是也惊慌失措过、懊恼过、心想,这什么鬼,谁TM干的!!!,如果返回code = 200还好,你可能就无视了,但是 月有阴晴圆缺、人有旦夕祸福、Http code 返回五花八门、保不齐你已经遇到过没返回200的OPTIONS
的请求了,那么想必现在的你定想搞清楚它,OK
咱今天就搞定它
6,OPTIONS:这个方法很有趣,但极少使用。它用于获取当前URL所支持的方法。若请求成功,则它会在HTTP头中包含一个名为“Allow”的头,值是所支持的方法,如“GET, POST”。
但问题是:什么时候会触发OPTIONS请求呢,明明你也没有调用它,它自己就请求了…….
回答上面问题就要先了解另一个问题-跨域资源共享 CORS
跨域资源共享 CORS
CORS是一个W3C标准,全称是”跨域资源共享”(Cross-origin resource sharing)。
它允许浏览器向跨源服务器,发出[XMLHttpRequest
]请求,从而克服了[AJAX]只能[同源]使用的限制。
一、简介
CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。
整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
二、两种请求
浏览器将CORS请求分成两类:
- 1.
简单请求(simple request)
- 2.
非简单请求(not-so-simple request
)
只要同时满足以下两大条件,就属于简单请求。
一、 请求方法是以下三种方法之一:
- HEAD
- GET
- POST
二、HTTP的头信息不超出以下几种字段:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type:只限于三个值
application/x-www-form-urlencoded
multipart/form-data
text/plain
凡是不同时满足
上面两个条件,就属于非简单请求。
浏览器对这两种请求的处理,是不一样的。
一、基本流程
对于简单请求,浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin
字段。
下面是一个例子,浏览器发现这次跨源AJAX请求是简单请求,就自动在头信息之中,添加一个Origin
字段。
GET / HTTP/1.1
Origin: https://loujinliang.cc
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0…
复制代码
上面的头信息中:
Origin
字段用来说明,本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求。Host
是要向哪个域请求
如果Origin
指定的源,不在目标服务器许可范围内,服务器会返回一个正常的HTTP回应。浏览器发现,这个回应的头信息没有包含Access-Control-Allow-Origin
字段(详见下文),就知道出错了,从而抛出一个错误,被XMLHttpRequest
的onerror
回调函数捕获。注意,这种错误无法通过状态码识别,因为HTTP回应的状态码有可能是200。
如果Origin
指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段。
Access-Control-Allow-Origin: https://loujinliang.cc
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8
复制代码
上面的头信息之中,有三个与CORS请求相关的字段,都以Access-Control-
开头。
(1)Access-Control-Allow-Origin
该字段是必须的。它的值要么是请求时Origin
字段的值,要么是一个*
,表示接受任意域名的请求。
(2)Access-Control-Allow-Credentials
该字段可选。它的值是一个布尔值,表示是否允许发送Cookie(服务端告诉客户端可不可以发送cookie)。默认情况下,Cookie不包括在CORS请求之中。设为true
,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为true
,如果服务器不要浏览器发送Cookie,删除该字段即可。
(3)[Access-Control-Max-Age](Access-Control-Max-Age – HTTP | MDN (mozilla.org))
该字段可选:(有一种情况就是你每一条非简单请求都会先发送预检请求,那就是这个字段没设置,我曾经在工作中就遇到过,当时没理会)
Access-Control-Max-Age: <delta-seconds>
复制代码
指令
-
返回结果可以被缓存的最长时间(秒)。
在 Firefox 中,上限是24小时 (即 86400 秒)。
在 Chromium v76 之前, 上限是 10 分钟(即 600 秒)。
从 Chromium v76 开始,上限是 2 小时(即 7200 秒)。
Chromium 同时规定了一个默认值 5 秒。
如果值为 -1,表示禁用缓存,则每次请求前都需要使用 OPTIONS 预检请求。
示例
将预检请求的结果缓存10分钟:
Access-Control-Max-Age: 600
复制代码
(4)Access-Control-Expose-Headers
该字段可选
响应首部 Access-Control-Expose-Headers
列出了哪些首部可以作为响应的一部分暴露给外部
这里有一个称呼叫:simple response headers(简单响应首部)6个字段
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
如果想要让客户端可以访问到其他的首部信息,可以将它们在 Access-Control-Expose-Headers
里面列出来
Access-Control-Expose-Headers: X-Custom-Header, x-loujinliang, X-xxxxx
复制代码
withCredentials 属性
上面说到,CORS请求默认不发送Cookie和HTTP认证信息。如果要把Cookie发到服务器,一方面要服务器同意,指定Access-Control-Allow-Credentials
字段。
Access-Control-Allow-Credentials: true
另一方面,开发者必须在AJAX请求中打开withCredentials
属性。
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
复制代码
// axios 设置方法
axios.defaults.withCredentials = true
复制代码
否则,即使服务器同意发送Cookie,浏览器也不会发送。或者,服务器要求设置Cookie,浏览器也不会处理。
但是,如果省略withCredentials
设置,有的浏览器还是会一起发送Cookie。这时,可以显式关闭withCredentials
。
xhr.withCredentials = false;
复制代码
// axios 设置方法
axios.defaults.withCredentials = false;
复制代码
需要注意的是,如果要发送Cookie,Access-Control-Allow-Origin
就不能设为星号,必须指定明确的、与请求网页一致的域名。同时,Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨源)原网页代码中的document.cookie
也无法读取服务器域名下的Cookie。
二、非简单请求
1、预检请求(OPTIONS)
非简单请求是那种对服务器有特殊要求的请求,
- 比如请求方法是
PUT
或DELETE
, - 或者
Content-Type
字段的类型是application/json
。
非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为”预检”请求(preflight)
浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP请求方法和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest
请求,否则就报错。
下面是一段浏览器的JavaScript脚本。
var url = 'https://textface666.com';
var xhr = new XMLHttpRequest();
xhr.open('PUT', url, true);
xhr.setRequestHeader('X-Custom-Header', 'value');
xhr.send();
复制代码
上面代码中,HTTP请求的方法是PUT
,并且发送一个自定义头信息X-Custom-Header
。
浏览器发现,这是一个非简单请求,就自动发出一个”预检”请求,要求服务器确认可以这样请求。下面是这个”预检”请求的HTTP头信息。
OPTIONS / HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: textface666.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0…
复制代码
相比与简单请求,在预简请求中,你要关注:Access-Control-Request-Method
字段
下面这句是告诉浏览器,服务器支持PUT请求方法:
Access-Control-Request-Method: PUT
复制代码
如果服务器支持多种方法,返回的会是这样 如下:
Access-Control-Request-Method: PUT, POST, GET, DELETE
复制代码
其它的上面已经说过,这里就不再赘述。
结尾
CORS与JSONP的使用目的相同,但是比JSONP更强大。
JSONP只支持GET
请求,CORS支持所有类型的HTTP请求。JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。
读完本文章的你,搞清楚Allow、 OPTIONS、 简单请求、 非简单请求、了吗,欢迎评论区讨论