對CORS OPTIONS預(yù)檢請求的一些思考

前后端分離模大勢所趨,跨域問題更是老生常談。
《程序員應(yīng)對瀏覽器同源策略的姿勢》一文提到三種跨域請求方案,重點講述了w3c和瀏覽器廠商推出的CORS規(guī)范。
同源策略??所謂同源是指域名、協(xié)議、端口相同。不同源的瀏覽器腳本(javascript、ActionScript、canvas)在沒有明確授權(quán)的情況下,不能讀寫對方的資源, 這是瀏覽器最基本的安全規(guī)范。
CORS是w3c和瀏覽器廠商為解決跨域資源共享問題而推出的標(biāo)準(zhǔn)方案:
瀏覽機(jī)器一旦發(fā)現(xiàn)跨域請求,就會自動添加一些附加的頭信息,有時還會多出一次附加的請求(瀏覽器自動完成,用戶不會察覺),服務(wù)器響應(yīng)特定標(biāo)頭Access-Control-,體現(xiàn)對跨源訪問的授權(quán)態(tài)度。
今天我主要想要聊一聊CORS中的預(yù)檢請求
當(dāng)前端使用腳本請求一個跨域資源時,如果是非簡單請求(下文會解釋),瀏覽器會自動幫你先發(fā)出一個OPTIONS查詢請求,稱為預(yù)檢(cors-preflight-request),作用是詢問服務(wù)器當(dāng)前網(wǎng)頁所在的域名是否在服務(wù)器的許可名單之中,以及可以使用哪些HTTP動詞和頭信息字段;只有得到肯定答復(fù),瀏覽器才會發(fā)出正式的跨域請求。

"預(yù)檢請求“的使用,可以避免跨域請求對服務(wù)器的用戶數(shù)據(jù)產(chǎn)生未預(yù)期的影響。
該請求header中會包含以下兩個字段:
Access-Control-Request-Method: 該字段的值對應(yīng)當(dāng)前請求類型,例如 GET、POST、PUT等等。瀏覽器會自動處理。
Access-Control-Request-Headers: 該字段的值對應(yīng)當(dāng)前請求可能會攜帶的額外的自定義header字段名,多個字段用逗號分割。瀏覽器會自動處理,將請求中非簡單的header字段全部列出來,例如標(biāo)識請求流水的x-request-id,用于Auth鑒權(quán)的Authorization 字段。
對于OPTIONS請求,按照規(guī)范實現(xiàn)的服務(wù)端會響應(yīng)一組HTTP header,但不會返回任何實體內(nèi)容。如果服務(wù)端支持該跨域請求,建議返回204狀態(tài)碼(返回200也可以);如果不支持,建議返回403狀態(tài)碼(返回404或其他錯誤狀態(tài)碼也可以)。
響應(yīng)的header可以包含以下字段:
Access-Control-Allow-Origin: 允許哪些域被允許跨域,例如 http://qq.com 或 https://qq.com,或者設(shè)置為* ,即允許所有域訪問
Access-Control-Allow-Credentials: 是否攜帶票據(jù)訪問(對應(yīng)fetch方法中credentials),當(dāng)該值為true時,Access-Control-Allow-Origin 不允許設(shè)置為*
Access-Control-Allow-Methods: 標(biāo)識該資源支持哪些方法,例如:POST, GET, PUT, DELETE
Access-Control-Allow-Headers: 標(biāo)識允許哪些額外的自定義 header 字段和非簡單值的字段
Access-Control-Max-Age: 表示可以緩存Access-Control-Allow-Methods和Access-Control-Allow-Headers提供的信息多長時間,單位秒,由服務(wù)端和瀏覽器默認(rèn)值共同決定。
Access-Control-Expose-Headers: 通過該字段指出哪些額外的 header 可以被支持。
由此可見,當(dāng)觸發(fā)預(yù)檢時,一次AJAX請求會消耗掉兩個TTL,嚴(yán)重影響性能。
那么如何節(jié)省掉OPTIONS請求來提升性能呢?從上文可以看出,有兩個方案:
發(fā)出簡單請求
只要同時滿足以下兩個條件,就屬于簡單請求
(1)使用下列方法之一:
head get post
(2)請求的Heder是
Accept Accept-Language Content-Language Content-Type: 只限于三個值:application/x-www-form-urlencoded、multipart/form-data、text/plain
不同時滿足上面的兩個條件,就屬于非簡單請求。很明顯,我們常見的Post請求且Content-Type=application/json也屬于非簡單請求,也會觸發(fā)預(yù)檢請求。
>? ?如果不方便改造為簡單請求,只有使用方案2了。
服務(wù)器端設(shè)置 Access-Control-Max-Age字段
當(dāng)?shù)谝淮握埱笤揢RL時會發(fā)出OPTIONS請求,瀏覽器會根據(jù)返回的Access-Control-Max-Age字段緩存該OPTIONS預(yù)檢請求的響應(yīng)結(jié)果。在緩存有效期內(nèi),該資源的請求(URL和header字段都相同的情況下)不會再觸發(fā)預(yù)檢。(chrome 打開控制臺可以看到,當(dāng)服務(wù)器響應(yīng)Access-Control-Max-Age時只有第一次請求會有預(yù)檢,后面不會了。注意要開啟緩存,去掉disable cache勾選)
但是要注意的是,該緩存只針對這一個請求 URL 和相同的 header,無法針對整個域或者模糊匹配 URL 做緩存(當(dāng)然也可以考慮封裝一下,固定一個接口地址,傳不同的body內(nèi)容)。
以上便是對CORS OPTIONS預(yù)檢請求的一些思考,希望對同學(xué)們有所幫助!
最后是Abp vNtext配置CORS的示例:
private?void?ConfigureCors(ServiceConfigurationContext?context,?IConfiguration?configuration)
{
?????context.Services.AddCors(options?=>
?????{
???????//?無阻塞跨域
????????options.AddPolicy(DefaultCorsPolicyName,?builder?=>
???????{
????????builder.SetIsOriginAllowed(_?=>?true)
?????????????.AllowCredentials()
?????????????.AllowAnyHeader()
?????????????.WithMethods(HttpMethods.Get,?HttpMethods.Post,?HttpMethods.Put,?HttpMethods.Delete)
????????????????????.SetPreflightMaxAge(TimeSpan.FromHours(24));
????????});
?????});
}
關(guān)注并星標(biāo)我們
更多干貨及最佳實踐分享
