前言:開(kāi)發(fā)時(shí)設(shè)置手機(jī)代理抓接口請(qǐng)求時(shí),打開(kāi)H5頁(yè)面,總是會(huì)看到OPTIONS請(qǐng)求,特此學(xué)習(xí)記錄一下。
1. 作用
-
請(qǐng)求服務(wù)器返回所支持的所有HTTP請(qǐng)求方法;
示例:
$ curl -X OPTIONS http://example.org -i
HTTP/1.1 200 OK
Allow: OPTIONS, GET, HEAD, POST #
Cache-Control: max-age=604800
Content-Type: text/html; charset=UTF-8
Date: Thu, 14 Jul 2022 06:15:30 GMT
Expires: Thu, 21 Jul 2022 06:15:30 GMT
Server: EOS (vny/0453)
Content-Length: 0
響應(yīng)報(bào)文包含一個(gè) Allow首部字段,該字段的值表明了服務(wù)器支持的所有 HTTP 方法。
-
跨域請(qǐng)求的預(yù)檢請(qǐng)求;
下面????會(huì)詳細(xì)介紹同源策略、跨域請(qǐng)求、OPTIONS請(qǐng)求。
2. 跨域請(qǐng)求
2.1 同源策略
如果兩個(gè)URL的協(xié)議、主機(jī)、端口都相同,則稱這兩個(gè)URL同源。以http://music.javaswing.cn/home/index.html為例:

作用:這種同源策略就是瀏覽器的一個(gè)安全機(jī)制,主要是限制html或者它加載的js在沒(méi)有明確授權(quán)的情況下,不能讀寫(xiě)其它源origin的資源,它能幫助阻隔惡意html,減少可能被攻擊的媒介。但是實(shí)際開(kāi)發(fā)時(shí),會(huì)有需要跨域的業(yè)務(wù),就有了CORS的出現(xiàn)。
2.2 CORS(Cross Origin Resource Sharing) 跨域資源共享
CORS 跨域資源共享是一個(gè)W3C標(biāo)準(zhǔn),允許瀏覽器向跨源服務(wù)器發(fā)送XMLHttpRequest請(qǐng)求。它使用額外的HTTP頭告訴瀏覽器,讓運(yùn)行在一個(gè)origin上的web應(yīng)用被準(zhǔn)許訪問(wèn)來(lái)自不同源服務(wù)器上的指定的資源。當(dāng)一個(gè)資源從與該資源本身所在的服務(wù)器不同的域、協(xié)議、或端口請(qǐng)求一個(gè)資源時(shí),資源會(huì)發(fā)起一個(gè)跨域HTTP請(qǐng)求。
簡(jiǎn)單來(lái)說(shuō),就是CORS允許在https://www.aa.com/a/a.html的網(wǎng)頁(yè)中,去請(qǐng)求https://www.bb.com/bb的接口獲取數(shù)據(jù)。
瀏覽器將CORS請(qǐng)求分成兩類:簡(jiǎn)單請(qǐng)求和非簡(jiǎn)單請(qǐng)求。
簡(jiǎn)單請(qǐng)求:
- 請(qǐng)求方法為:HEAD/GET/POST
- HTTP的頭信息不超過(guò)以下幾個(gè)字段:Accept、Accept-Language、Content-Language、Last-Event-ID、Content-Type(只限于三個(gè)值application/x-www-form-urlencoded、mutipart/form-data、text/plain)
非簡(jiǎn)單請(qǐng)求:
凡是不同時(shí)滿足上面兩個(gè)條件的,就屬于非簡(jiǎn)單請(qǐng)求。
瀏覽器對(duì)這兩種請(qǐng)求的處理是不一樣的:
簡(jiǎn)單請(qǐng)求:
- 在請(qǐng)求中需要附加一個(gè)額外的Origin頭部,其中包含請(qǐng)求頁(yè)面的源信息(協(xié)議、域名、端口號(hào)),便于服務(wù)器根據(jù)這個(gè)頭部信息決定是否給予響應(yīng);
- 如果服務(wù)器認(rèn)為這個(gè)請(qǐng)求可以接口,就在Access-Control-Allow-Origin頭部中回發(fā)相同的源信息(如果是公共資源,可以設(shè)置 *);
- 沒(méi)有這個(gè)頭部或者源信息不匹配,瀏覽器就會(huì)駁回請(qǐng)求,正常情況下,瀏覽器會(huì)處理請(qǐng)求。注意:請(qǐng)求和響應(yīng)都不包含cookie信息;
- 如果需要包含cookie信息,ajax請(qǐng)求需要設(shè)置xhr的屬性withCredentials為true,服務(wù)器需要設(shè)置響應(yīng)頭部 Access-Control-Allow-Credentials: true;
非簡(jiǎn)單請(qǐng)求:
瀏覽器在發(fā)送真正的請(qǐng)求之前,會(huì)先發(fā)送一個(gè)Preflight請(qǐng)求給服務(wù)器,這種請(qǐng)求使用OPTIONS方法,下面會(huì)展開(kāi)進(jìn)行介紹。
3. OPTIONS預(yù)檢請(qǐng)求
3.1 OPTIONS預(yù)檢請(qǐng)求的觸發(fā)條件
上面了解了同源策略和CORS,終于到了OPTIONS請(qǐng)求。在CORS機(jī)制下一個(gè)域名A要訪問(wèn)域名B的服務(wù),在使用非簡(jiǎn)單請(qǐng)求之前,會(huì)先進(jìn)行一個(gè)預(yù)檢請(qǐng)求(瀏覽器自動(dòng)發(fā)起),檢查B服務(wù)是否允許跨域請(qǐng)求,服務(wù)確認(rèn)之后,才會(huì)發(fā)起真正的HTTP請(qǐng)求。
同時(shí),在預(yù)檢請(qǐng)求的返回中,服務(wù)端也可以通知客戶端,是否需要攜帶身份憑證(包括Cookies和HTTP認(rèn)證相關(guān)數(shù)據(jù))。
3.2 OPTIONS預(yù)檢請(qǐng)求結(jié)構(gòu)
3.2.1 OPTIONS預(yù)檢請(qǐng)求頭
OPTIONS預(yù)檢請(qǐng)求會(huì)攜帶幾個(gè)關(guān)鍵的Request Header:
| Request Header | 作用 |
|---|---|
| Access-Control-Request-Method | 告訴服務(wù)器實(shí)際請(qǐng)求所使用的 HTTP 方法 |
| Access-Control-Request-Headers | (可選)告訴服務(wù)器實(shí)際請(qǐng)求所攜帶的自定義首部字段 |
| Origin | 發(fā)起請(qǐng)求的域名 (協(xié)議、域名、端口號(hào)),便于服務(wù)器根據(jù)這個(gè)頭部信息決定是否給予響應(yīng) |
{
"Host": "app.aaa.com",
"Origin": "https://m.bbb.com",
"Access-Control-Request-Method": "GET",
"Access-Control-Request-Headers": "csrf-token",
"Connection": "keep-alive",
"Accept": "*/*",
"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 zzhunter",
"Referer": "https://m.caihuoxia.com/",
"Accept-Language": "zh-CN,zh-Hans;q=0.9",
"Accept-Encoding": "gzip"
}
3.2.2 OPTIONS預(yù)檢響應(yīng)頭
當(dāng)收到一個(gè)預(yù)檢請(qǐng)求之后,服務(wù)器可以決定是否允許這種類型的請(qǐng)求。服務(wù)器通過(guò)在響應(yīng)中發(fā)送如下頭部信息與瀏覽器進(jìn)行溝通。預(yù)檢響應(yīng)頭的關(guān)鍵字段:
| response header | 作用 |
|---|---|
| Access-Control-Allow-Methods | 返回了服務(wù)端允許的請(qǐng)求,包含 GET/HEAD/PUT/PATCH/POST/DELETE |
| Access-Control-Allow-Credentials | 允許跨域攜帶 cookie(跨域請(qǐng)求要攜帶 cookie 必須設(shè)置為 true) |
| Access-Control-Allow-Origin | 允許跨域請(qǐng)求的域名,這個(gè)可以在服務(wù)端配置一些信任的域名白名單 |
| Access-Control-Allow-Headers | 客戶端請(qǐng)求所攜帶的自定義首部字段 |
| Access-Control-Max-Age | 應(yīng)該將這個(gè) Preflight 請(qǐng)求緩存多長(zhǎng)時(shí)間(以秒表示) |
{
"Server": "Tengine",
"Date": "Thu, 14 Jul 2022 06:25:07 GMT",
"Content-Type": "text/plain; charset=utf-8",
"Content-Length": "0",
"Connection": "keep-alive",
"Access-Control-Allow-Origin": "https://m.bbb.com",
"Access-Control-Allow-Credentials": "true",
"Access-Control-Allow-Headers": "PPU,t,tk,v,uid,Content-Type,Csrf-Token",
"Access-Control-Allow-Methods": "GET,POST,OPTIONS",
"Access-Control-Max-Age": "1728000",
"Set-Cookie": [
"id58=c5/nR2LPtsOJbsB8a+YHAg==; expires=Sat, 13-Jul-24 06:25:07 GMT; domain=caihuoxia.com; path=/"
],
"P3P": "policyref=\"/w3c/p3p.xml\", CP=\"CUR ADM OUR NOR STA NID\""
}
一旦服務(wù)器通過(guò)Preflight 請(qǐng)求允許該請(qǐng)求之后,以后每次瀏覽器正常的CORS請(qǐng)求,都跟簡(jiǎn)單請(qǐng)求一樣了。
4. OPTIONS預(yù)檢請(qǐng)求優(yōu)化
上面了解到,跨域請(qǐng)求會(huì)觸發(fā)兩次請(qǐng)求:OPTIONS預(yù)檢請(qǐng)求、真正的請(qǐng)求。可以通過(guò)緩存OPTIONS預(yù)檢請(qǐng)求的結(jié)果,來(lái)減少請(qǐng)求的數(shù)量。
Access-Control-Max-Age 這個(gè)響應(yīng)首部表示預(yù)檢請(qǐng)求的返回結(jié)果(即 Access-Control-Allow-Methods 和 Access-Control-Allow-Headers 提供的信息) 可以被緩存的最長(zhǎng)時(shí)間,單位是秒。
如果值為 -1,則表示禁用緩存,每一次請(qǐng)求都需要提供預(yù)檢請(qǐng)求,即用 OPTIONS 請(qǐng)求進(jìn)行檢測(cè)。
5. 總結(jié)
OPTIONS預(yù)檢請(qǐng)求,可用于檢測(cè)服務(wù)器允許的http方法。另外,當(dāng)發(fā)起跨域請(qǐng)求時(shí),非簡(jiǎn)單請(qǐng)求會(huì)導(dǎo)致瀏覽器自動(dòng)發(fā)起一次OPTIONS預(yù)檢請(qǐng)求,服務(wù)器接受該跨域請(qǐng)求之后,瀏覽器才繼續(xù)發(fā)起正式請(qǐng)求。
CORS的優(yōu)點(diǎn):
- CORS通信與同源的AJAX通信沒(méi)有差別,代碼完全一樣,容易維護(hù);
- 支持所有類型的HTTP請(qǐng)求;
CORS的缺點(diǎn):
- 存在兼容性問(wèn)題,特別是IE10以下的瀏覽器;
- 第一次發(fā)送非簡(jiǎn)單請(qǐng)求時(shí)會(huì)多一次請(qǐng)求;