ios 请求头设置token_HTTP中的OPTIONS请求
前言
http請求之前已經接觸了很多,但是這個options請求我還是第一次,剛來到公司的時候進行調試,發現NetWork里,每個請求在發出之前都會先發送一個options請求,第二個才是正常的請求。先來看下MDN官方的解釋。
MDN
HTTP 的 OPTIONS 方法 用于獲取目的資源所支持的通信選項。客戶端可以對特定的 URL 使用 OPTIONS 方法,也可以對整站(通過將 URL 設置為“*”)使用該方法。
作用:
出現原因
在說OPTIONS請求出的原因之前,要先說下瀏覽器的同源策略和跨域資源共享 CORS
同源策略
如果兩個URL的協議(protocol)、端口(port)、主機(host),都相同,則稱這個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 | 不同源 | 協議不同 |
| http://api.javaswing.cn/start.html | 不同源 | 主機不同 |
作用:同源策略的存在,主要是為用于限制文檔與它加載的腳本如何能與另一個資源進行交互,為重要的安全策略。
比如:你本地http://localhost:3000的項目訪問http://localhost:8000的項目,就會出現: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) 是一種機制,它使用額外的 HTTP 頭來告訴瀏覽器 ?讓運行在一個 origin (domain) 上的Web應用被準許訪問來自不同源服務器上的指定的資源。當一個資源從與該資源本身所在的服務器不同的域、協議或端口請求一個資源時,資源會發起一個跨域 HTTP 請求。
簡單的來說:CORS就是兩種在不同的域、協議或端口(即不在同源中),服務之間能相互訪問。
OPTIONS請求
說完了同源策略和CORS,接下來說下OPTIONS請求。在CORS機制一個域名A要訪問域名B的服務,在一些特殊的復雜請求下(簡單請求并不會進行預請求),瀏覽器必須先使用OPTIONS請求進行一個預檢請求(preflight request)來獲取B服務是否允許跨域請求,服務進行確認之后,才會發起真正的HTTP請求。在預檢請求的返回中,服務器端也可以通知客戶端,是否需要攜帶身份憑證(包括 Cookies 和 HTTP 認證相關數據)。
簡單請求
- 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
實例
環境:node 10.x ?koa ? vue
瀏覽器:火狐
本地vue環境:localhost:8080
本地koa地址:localhost:3000
app.use(async?(ctx,?next)?=>?{
????//?允許來息所有域名的請求
????ctx.set('Access-Control-Allow-Origin',?'*')
????
????//?允許HTTP請求的方法
????ctx.set('Access-Control-Allow-Methods',?'OPTIONS,DELETE,GET,PUT,POST')
????
????//?表明服務器支持所有頭信息字段
????ctx.set('Access-Control-Allow-Headers',?'x-requested-with,?accept,?origin,?content-type,?token')
????
????//?Content-Type表示具體請求中的媒體類型信息
????ctx.set('Content-Type',?'application/json;charset=utf-8')
????
????await?next()
})
- vue層面簡單的GET請求
????return?axios({
????????url:?`//localhost:3000`,
????????method:?'get'
????})
}
結果如下圖:
image從圖中可以看到結果為正常的請求,并沒有進行發出OPTIONS請求進行預檢測。
- 修改vue中的請求頭部分,添加一個header.token字段:
????return?axios({
????????url:?`//localhost:3000`,
????????method:?'get',
????????headers:?{token:?'test'}
????})
}
這里的GET請求,在HEADE中設置了token字段,不屬于簡單請求。所以發出了OPTIONS請求。但是,如果你只設置了ctx的頭,你會發現,請求還是會報錯
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。說明的很清楚,就是options請求沒有正確的響應。
解決方法有兩種:
????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))?{
??????????//?服務器不支持該方法的情況
??????????if?(options.throw)?{
????????????var?notImplementedThrowable;
????????????if?(typeof?options.notImplemented?===?'function')?{
??????????????notImplementedThrowable?=?options.notImplemented();
????????????}?else?{
??????????????notImplementedThrowable?=?new?HttpError.NotImplemented();
????????????}
????????????throw?notImplementedThrowable;
??????????}?else?{
????????????//?響應?501?Not?Implemented
????????????ctx.status?=?501;
????????????ctx.set('Allow',?allowedArr.join(',?'));
??????????}
????????}?else?if?(allowedArr.length)?{
??????????if?(ctx.method?===?'OPTIONS')?{
????????????//?獲取服務器對該路由路徑支持的方法集合
????????????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?{
??????????????//?響應?405?Method?Not?Allowed
??????????????ctx.status?=?405;
??????????????ctx.set('Allow',?allowedArr.join(',?'));
????????????}
??????????}
????????}
??????}
????});
??};
};
可以看到在這個方法里當請求方式為OPTIONS會進行正常的返回處理。
設置這個方法之后,再進行請求:
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 響應首部字段解釋表:
CORS請求相關的字段,都以Access-Control-開頭
| Access-Control-Allow-Origin | Access-Control-Allow-Origin:或 * | orgin指定允許訪問該資源的URL,設置為*則為任意 |
| Access-Control-Allow-Methods | Access-Control-Allow-Methods:[,]* | 用于預檢測請求響應,告訴瀏覽器實際請求支持的方法 |
| Access-Control-Allow-Headers | Access-Control-Allow-Headers:[,]* | 用于預檢測請求響應,告訴瀏覽器實際請求中允許攜帶的字段 |
| Access-Control-Max-Age | Access-Control-Max-Age: | 指定瀏覽器preflight請求能被緩存多長時間,單位(秒) |
| Access-Control-Allow-Credentials | Access-Control-Allow-Credentials: true | 當瀏覽器的credentials設置為true時是否允許瀏覽器讀取response的內容。在XMLHttpRequest中設置withCredentials為true,且設置了該屬性,則會帶到身份Cookies。如果Access-Control-Allow-Origin為*的,這里的一切設置都會失效。 |
如何優化
如果不想讓每個CORS復雜請求都出兩次請求,可以設置Access-Control-Max-Age這個屬性。讓瀏覽器緩存,在緩存的有效期內,所有options請求都不會發送。優化性能。
app.use(async?(ctx,?next)?=>?{????//?允許來息所有域名的請求
????ctx.set('Access-Control-Allow-Origin',?'*')
????//?允許HTTP請求的方法
????ctx.set('Access-Control-Allow-Methods',?'OPTIONS,DELETE,GET,PUT,POST')
????//?表明服務器支持所有頭信息字段
????ctx.set('Access-Control-Allow-Headers',?'x-requested-with,?accept,?origin,?content-type,?token')
????//?設置請求preflight緩存的時間,單位?秒
????ctx.set('Access-Control-Max-Age',?10)
????})
其它問題
這里我測試的時候遇到一個問題:在火狐瀏覽器上options請求能顯示出來,但是在chrome瀏覽器里就不能顯示,不知道為什么
總結
在當前,前后端分離的開發模式下,跨域問題是經常遇到的,OPTIONS只不過CORS機制當中的一個預檢測請求。而且這個請求是整個CORS機制控制的,并不能在前端用代碼進行控制。主要作用:
另外給個在線的curl命令工具:https://reqbin.com/req/jecm0tqu/options-request-example
參考
- MDN OPTIONS請求
- HTTP訪問控制(CORS)
- 跨域資源共享 CORS 詳解- 跨域資源共享 CORS 詳解
- 玩轉Koa -- koa-router
總結
以上是生活随笔為你收集整理的ios 请求头设置token_HTTP中的OPTIONS请求的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: AI小姐姐比真人还好看? N卡又抓到风口
- 下一篇: 从青海湖电池到超越华为Mate 赵明专访