不久前在公司寫了一個基於 Hapijs 的後端項目,感受這個框架頗有本身的特色,跟 Express 和 Koa 的區別比較大,體現了配置大於編碼的思想。用起來很方便,聽說 Walmart 團隊用這個框架扛住了黑五的流量,看起來在實際項目中也有可用性,推薦你們嘗試一下~html
有點跑題了,這篇文章主要寫我在開發過程當中所遇到的一個問題,以及我從這個問題所學習到的東西,而後我是怎麼解決這個問題的。前端
個人項目需求是寫一個 App 版本管理器,先後端都由我開發。前端分爲兩個部分:運營人員寫版本更新說明的內部系統和 App 訪問的產品頁;後端就是對 App 版本進行管理的 CURD 接口。重點在於三個部分的程序部署在三臺服務器上,前端的兩個系統在不一樣的服務器對第三個服務器上的接口進行數據請求,這就不可避免的涉及到了跨域。程序員
固然,只是跨域的話也不難解決,添加 Access-Control-Allow-Origin
爲要跨域的域名就 OK 了,或者直接賦值爲 *
。可是個人部分接口涉及鑑權,經過 JWT 進行校驗,若是 JWT 不合法,那麼會返回 401 Unauthorized 錯誤;而個人 JWT 是經過請求頭的自定義字段 authorization
帶到服務器的,這就致使一個更加麻煩的問題出現了 —— 預檢請求。segmentfault
預檢請求(preflight request),是一個跨域請求,用來校驗當前跨域請求可否被理解。後端
它使用 HTTP 的 OPTIONS 請求,通常會包括一下請求頭:Access-Control-Request-Method
,Access-Control-Request-Headers
和 Origin
。api
預檢請求一般在必要的時候由瀏覽器自動發起,不須要程序員進行干預。跨域
若是咱們想要知道服務器是否支持一個 DELETE
請求,在發送 DELETE
請求以前,服務器一般會發送一個以下的預檢請求:瀏覽器
OPTIONS /resource/foo
Access-Control-Request-Method: DELETE
Access-Control-Request-Headers: origin, x-requested-with
Origin: https://foo.bar.org
複製代碼
若是服務器容許使用 DELETE
方法的話,會返回以下響應頭;其中 Access-Control-Allow-Methods
會列出 DELETE
方法,表明服務器支持這個方法。bash
HTTP/1.1 200 OK
Content-Length: 0
Connection: keep-alive
Access-Control-Allow-Origin: https://foo.bar.org
Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE
Access-Control-Max-Age: 86400
複製代碼
以上資料來源於 MDN服務器
由此可知,預檢請求是一個用於校驗服務器是否支持當前方法以及是否可以理解當前請求的一種請求,它區別於通常的請求,不禁代碼發起,而在必要的時候由瀏覽器自動發出。
因此這裏就出問題了,若是咱們不知道何時瀏覽器會發出預檢請求,那麼服務器沒有作處理的話就會致使 CORS 報錯的出現。
接下來再深刻一點。
知足如下條件的請求就是簡單請求:
1、請求方法屬於下面三種方法之一:
2、HTTP 的請求頭信息超出一下範圍:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:超出這三個的範圍:
不知足以上條件的請求就是非簡單請求。
若是是簡單的 CORS 請求,瀏覽器會自動在請求頭中添加一個 Origin 請求頭字段,若是響應頭對應的 Access-Control-Allow-Origin
沒有包含 Origin 所指定的域,那麼就會報 CORS 錯誤,請求失敗。因此服務器的響應要添加對應的響應頭。
若是是非簡單的 CORS 請求,那麼會有一次預檢請求,在正是請求以前發出一個 OPTIONS 請求對服務器進行檢測。
除了有 Origin 之外,預檢請求的請求頭還包括一下兩個特殊字段:
Access-Control-Request-Method
:表示 CORS 請求要用到的請求方法。
Access-Control-Request-Headers
:這是一個用逗號分割的字符串,指出 CORS 請求要附加的請求頭。
服務器的響應能夠包含如下字段:
Access-Control-Allow-Methods
:逗號分割的字符串,表示容許的跨域請求方法。
好比:
Access-Control-Allow-Methods: PUT, POST, GET, OPTIONS
複製代碼
Access-Control-Allow-Headers
:若是瀏覽器請求包含 Access-Control-Request-Headers
字段,那麼服務器中該響應頭也是必須的,也是一個由逗號分隔的字符串,表示服務器支持的請求頭。
好比:
Access-Control-Allow-Headers: authorization
複製代碼
Access-Control-Max-Age
:可選字段,設置當前預檢請求的有效期,單位爲秒。
Access-Control-Allow-Credentials
:可選字段。默認狀況下,CORS 請求不攜帶 cookie,若是服務器想要 cookie,須要指定該請求頭爲 true
。
避免出現預檢請求,須要使得你的請求知足簡單請求的兩個條件。
好比在使用 JWT 鑑權時,可能會把你的 token 放在請求頭的 authorization 字段,由於這個字段超出了簡單請求的範圍,因此請求會變成非簡單請求。這時能夠不把 token 放在 authorization 請求頭中。
出現預檢請求後,進行服務器配置,分別設置好 Access-Control-Allow-Origin
、Access-Control-Allow-Methods
和 Access-Control-Allow-Headers
,使得你的非簡單請求可以經過預檢請求。
若是使用 Hapijs 的話,只須要在路由配置中增長 cors: true
配置便可。