ios 请求头设置token_HTTP中的OPTIONS请求
前言
http請(qǐng)求之前已經(jīng)接觸了很多,但是這個(gè)options請(qǐng)求我還是第一次,剛來(lái)到公司的時(shí)候進(jìn)行調(diào)試,發(fā)現(xiàn)NetWork里,每個(gè)請(qǐng)求在發(fā)出之前都會(huì)先發(fā)送一個(gè)options請(qǐng)求,第二個(gè)才是正常的請(qǐng)求。先來(lái)看下MDN官方的解釋。
MDN
HTTP 的 OPTIONS 方法 用于獲取目的資源所支持的通信選項(xiàng)。客戶(hù)端可以對(duì)特定的 URL 使用 OPTIONS 方法,也可以對(duì)整站(通過(guò)將 URL 設(shè)置為“*”)使用該方法。
作用:
出現(xiàn)原因
在說(shuō)OPTIONS請(qǐng)求出的原因之前,要先說(shuō)下瀏覽器的同源策略和跨域資源共享 CORS
同源策略
如果兩個(gè)URL的協(xié)議(protocol)、端口(port)、主機(jī)(host),都相同,則稱(chēng)這個(gè)URL為同源。以http://music.javaswing.cn/home/index.html為例子:
| http://music.javaswing.cn/static/other.html | 同源 | |
| http://music.javaswng.cn/inner/start.html | 同源 | |
| http://music.javaswing.cn:8000/other.html | 不同源 | 端口不同 |
| https://music.javaswing.cn/inner/start.html | 不同源 | 協(xié)議不同 |
| http://api.javaswing.cn/start.html | 不同源 | 主機(jī)不同 |
作用:同源策略的存在,主要是為用于限制文檔與它加載的腳本如何能與另一個(gè)資源進(jìn)行交互,為重要的安全策略。
比如:你本地http://localhost:3000的項(xiàng)目訪問(wèn)http://localhost:8000的項(xiàng)目,就會(huì)出現(xiàn):has been blocked by CORS policy
Access?to?XMLHttpRequest?at?'http://localhost:3000/'?from?origin?'http://localhost:8080'?has?been?blocked?by?CORS?policy:?Response?to?preflight?request?doesn't?pass?access?control?check:?No?'Access-Control-Allow-Origin'?header?is?present?on?the?requested?resource.CORS
跨域資源共享(CORS) 是一種機(jī)制,它使用額外的 HTTP 頭來(lái)告訴瀏覽器 ?讓運(yùn)行在一個(gè) origin (domain) 上的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就是兩種在不同的域、協(xié)議或端口(即不在同源中),服務(wù)之間能相互訪問(wèn)。
OPTIONS請(qǐng)求
說(shuō)完了同源策略和CORS,接下來(lái)說(shuō)下OPTIONS請(qǐng)求。在CORS機(jī)制一個(gè)域名A要訪問(wèn)域名B的服務(wù),在一些特殊的復(fù)雜請(qǐng)求下(簡(jiǎn)單請(qǐng)求并不會(huì)進(jìn)行預(yù)請(qǐng)求),瀏覽器必須先使用OPTIONS請(qǐng)求進(jìn)行一個(gè)預(yù)檢請(qǐng)求(preflight request)來(lái)獲取B服務(wù)是否允許跨域請(qǐng)求,服務(wù)進(jìn)行確認(rèn)之后,才會(huì)發(fā)起真正的HTTP請(qǐng)求。在預(yù)檢請(qǐng)求的返回中,服務(wù)器端也可以通知客戶(hù)端,是否需要攜帶身份憑證(包括 Cookies 和 HTTP 認(rèn)證相關(guān)數(shù)據(jù))。
簡(jiǎn)單請(qǐng)求
- GET
- HEAD
- POST
- Accept
- Accept-Language
- Content-Language
- Content-Type (需要注意額外的限制)
- DPR
- Downlink
- Save-Data
- Viewport-Width
- Width
- text/plain
- multipart/form-data
- application/x-www-form-urlencoded
實(shí)例
環(huán)境:node 10.x ?koa ? vue
瀏覽器:火狐
本地vue環(huán)境:localhost:8080
本地koa地址:localhost:3000
app.use(async?(ctx,?next)?=>?{
????//?允許來(lái)息所有域名的請(qǐng)求
????ctx.set('Access-Control-Allow-Origin',?'*')
????
????//?允許HTTP請(qǐng)求的方法
????ctx.set('Access-Control-Allow-Methods',?'OPTIONS,DELETE,GET,PUT,POST')
????
????//?表明服務(wù)器支持所有頭信息字段
????ctx.set('Access-Control-Allow-Headers',?'x-requested-with,?accept,?origin,?content-type,?token')
????
????//?Content-Type表示具體請(qǐng)求中的媒體類(lèi)型信息
????ctx.set('Content-Type',?'application/json;charset=utf-8')
????
????await?next()
})
- vue層面簡(jiǎn)單的GET請(qǐng)求
????return?axios({
????????url:?`//localhost:3000`,
????????method:?'get'
????})
}
結(jié)果如下圖:
image從圖中可以看到結(jié)果為正常的請(qǐng)求,并沒(méi)有進(jìn)行發(fā)出OPTIONS請(qǐng)求進(jìn)行預(yù)檢測(cè)。
- 修改vue中的請(qǐng)求頭部分,添加一個(gè)header.token字段:
????return?axios({
????????url:?`//localhost:3000`,
????????method:?'get',
????????headers:?{token:?'test'}
????})
}
這里的GET請(qǐng)求,在HEADE中設(shè)置了token字段,不屬于簡(jiǎn)單請(qǐng)求。所以發(fā)出了OPTIONS請(qǐng)求。但是,如果你只設(shè)置了ctx的頭,你會(huì)發(fā)現(xiàn),請(qǐng)求還是會(huì)報(bào)錯(cuò)
Access?to?XMLHttpRequest?at?'http://localhost:3000/'?from?origin?'http://localhost:8080'?has?been?blocked?by?CORS?policy:?Response?to?preflight?request?doesn't?pass?access?control?check:?It?does?not?have?HTTP?ok?status.image
preflight request doesn't pass access control check。說(shuō)明的很清楚,就是options請(qǐng)求沒(méi)有正確的響應(yīng)。
解決方法有兩種:
????console.log('options');
????ctx.body?=?''
})
從源碼看allowedMethods
Router.prototype.allowedMethods?=?function?(options)?{??options?=?options?||?{};
??var?implemented?=?this.methods;
??return?function?allowedMethods(ctx,?next)?{
????return?next().then(function()?{
??????var?allowed?=?{};
??????if?(!ctx.status?||?ctx.status?===?404)?{
????????ctx.matched.forEach(function?(route)?{
??????????route.methods.forEach(function?(method)?{
????????????allowed[method]?=?method;
??????????});
????????});
????????var?allowedArr?=?Object.keys(allowed);
????????if?(!~implemented.indexOf(ctx.method))?{
??????????//?服務(wù)器不支持該方法的情況
??????????if?(options.throw)?{
????????????var?notImplementedThrowable;
????????????if?(typeof?options.notImplemented?===?'function')?{
??????????????notImplementedThrowable?=?options.notImplemented();
????????????}?else?{
??????????????notImplementedThrowable?=?new?HttpError.NotImplemented();
????????????}
????????????throw?notImplementedThrowable;
??????????}?else?{
????????????//?響應(yīng)?501?Not?Implemented
????????????ctx.status?=?501;
????????????ctx.set('Allow',?allowedArr.join(',?'));
??????????}
????????}?else?if?(allowedArr.length)?{
??????????if?(ctx.method?===?'OPTIONS')?{
????????????//?獲取服務(wù)器對(duì)該路由路徑支持的方法集合
????????????ctx.status?=?200;
????????????ctx.body?=?'';
????????????ctx.set('Allow',?allowedArr.join(',?'));
??????????}?else?if?(!allowed[ctx.method])?{
????????????if?(options.throw)?{
??????????????var?notAllowedThrowable;
??????????????if?(typeof?options.methodNotAllowed?===?'function')?{
????????????????notAllowedThrowable?=?options.methodNotAllowed();
??????????????}?else?{
????????????????notAllowedThrowable?=?new?HttpError.MethodNotAllowed();
??????????????}
??????????????throw?notAllowedThrowable;
????????????}?else?{
??????????????//?響應(yīng)?405?Method?Not?Allowed
??????????????ctx.status?=?405;
??????????????ctx.set('Allow',?allowedArr.join(',?'));
????????????}
??????????}
????????}
??????}
????});
??};
};
可以看到在這個(gè)方法里當(dāng)請(qǐng)求方式為OPTIONS會(huì)進(jìn)行正常的返回處理。
設(shè)置這個(gè)方法之后,再進(jìn)行請(qǐng)求:
imageHTTP/1.1?200?OK
Access-Control-Allow-Origin:?*
Access-Control-Allow-Methods:?OPTIONS,DELETE,GET,PUT,POST
Access-Control-Allow-Headers:?x-requested-with,?accept,?origin,?content-type,?to
ken
Content-Type:?application/json;charset=utf-8
Content-Length:?0
Allow:?HEAD,?GET,?POST,?PUT
Date:?Sat,?01?Aug?2020?11:14:53?GMT
Connection:?keep-alive
HTTP 響應(yīng)首部字段解釋表:
CORS請(qǐng)求相關(guān)的字段,都以Access-Control-開(kāi)頭
| Access-Control-Allow-Origin | Access-Control-Allow-Origin:或 * | orgin指定允許訪問(wèn)該資源的URL,設(shè)置為*則為任意 |
| Access-Control-Allow-Methods | Access-Control-Allow-Methods:[,]* | 用于預(yù)檢測(cè)請(qǐng)求響應(yīng),告訴瀏覽器實(shí)際請(qǐng)求支持的方法 |
| Access-Control-Allow-Headers | Access-Control-Allow-Headers:[,]* | 用于預(yù)檢測(cè)請(qǐng)求響應(yīng),告訴瀏覽器實(shí)際請(qǐng)求中允許攜帶的字段 |
| Access-Control-Max-Age | Access-Control-Max-Age: | 指定瀏覽器preflight請(qǐng)求能被緩存多長(zhǎng)時(shí)間,單位(秒) |
| Access-Control-Allow-Credentials | Access-Control-Allow-Credentials: true | 當(dāng)瀏覽器的credentials設(shè)置為true時(shí)是否允許瀏覽器讀取response的內(nèi)容。在XMLHttpRequest中設(shè)置withCredentials為true,且設(shè)置了該屬性,則會(huì)帶到身份Cookies。如果Access-Control-Allow-Origin為*的,這里的一切設(shè)置都會(huì)失效。 |
如何優(yōu)化
如果不想讓每個(gè)CORS復(fù)雜請(qǐng)求都出兩次請(qǐng)求,可以設(shè)置Access-Control-Max-Age這個(gè)屬性。讓瀏覽器緩存,在緩存的有效期內(nèi),所有options請(qǐng)求都不會(huì)發(fā)送。優(yōu)化性能。
app.use(async?(ctx,?next)?=>?{????//?允許來(lái)息所有域名的請(qǐng)求
????ctx.set('Access-Control-Allow-Origin',?'*')
????//?允許HTTP請(qǐng)求的方法
????ctx.set('Access-Control-Allow-Methods',?'OPTIONS,DELETE,GET,PUT,POST')
????//?表明服務(wù)器支持所有頭信息字段
????ctx.set('Access-Control-Allow-Headers',?'x-requested-with,?accept,?origin,?content-type,?token')
????//?設(shè)置請(qǐng)求preflight緩存的時(shí)間,單位?秒
????ctx.set('Access-Control-Max-Age',?10)
????})
其它問(wèn)題
這里我測(cè)試的時(shí)候遇到一個(gè)問(wèn)題:在火狐瀏覽器上options請(qǐng)求能顯示出來(lái),但是在chrome瀏覽器里就不能顯示,不知道為什么
總結(jié)
在當(dāng)前,前后端分離的開(kāi)發(fā)模式下,跨域問(wèn)題是經(jīng)常遇到的,OPTIONS只不過(guò)CORS機(jī)制當(dāng)中的一個(gè)預(yù)檢測(cè)請(qǐng)求。而且這個(gè)請(qǐng)求是整個(gè)CORS機(jī)制控制的,并不能在前端用代碼進(jìn)行控制。主要作用:
另外給個(gè)在線(xiàn)的curl命令工具:https://reqbin.com/req/jecm0tqu/options-request-example
參考
- MDN OPTIONS請(qǐng)求
- HTTP訪問(wèn)控制(CORS)
- 跨域資源共享 CORS 詳解- 跨域資源共享 CORS 詳解
- 玩轉(zhuǎn)Koa -- koa-router
總結(jié)
以上是生活随笔為你收集整理的ios 请求头设置token_HTTP中的OPTIONS请求的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: AI小姐姐比真人还好看? N卡又抓到风口
- 下一篇: 室内装修隐蔽工程验收知识拓展_装修之前先