我通過這篇文章把今天工作中遇到的HTTP跨域和OPTION請求的一個坑記錄下來。 場景是我需要在部署在域名a的Web應用里用JavaScript去消費一個部署在域名b的服務器上的服務。域名b上的服務也是我開發(fā)的,因此我將域名a加到了該服務的HTTP響應結構的頭文件里,這樣就允許了域名a上的JavaScript代碼用AJAX訪問域名b的服務。 域名b上的服務是一個Servlet,允許域名a跨域訪問的代碼就一行:
我在域名a的Web應用里用AJAX發(fā)起服務請求: 執(zhí)行后,發(fā)現(xiàn)并沒有顯示200的彈出窗口。 錯誤消息:Request header field Authorization is not allowed by Access-Control-Allow-Headers in preflight response. 觀察Chrome開發(fā)者工具,發(fā)現(xiàn)其實域名b的服務已經(jīng)成功執(zhí)行了,確實返回了200的Status code, 而且我已經(jīng)從Chrome開發(fā)者工具里觀察到瀏覽器已經(jīng)成功接到域名b發(fā)送回來的請求了。 那這個錯誤是什么鬼呢?根據(jù)錯誤消息“Request header field Authorization is not allowed by Access-Control-Allow-Headers in preflight response” Google了一下,發(fā)現(xiàn)一些朋友遇到同樣的問題:
網(wǎng)頁地址: https://www.cnblogs.com/caimuqing/p/6733405.html 這位朋友的解決方案:
但我試過,在我的場景下還是不工作,因為我的例子里,服務器已經(jīng)針對OPTIONS請求返回HTTP 200的狀態(tài)碼了。 2. 這個Stackoverflow的帖子里,很多朋友都提供了自己的解決方案。 https:///questions/42061727/cors-error-request-header-field-authorization-is-not-allowed-by-access-control/42061962 我一一試過,在我的場景里都不能工作。 于是我查詢了Mozilla的一篇文檔:HTTP訪問控制(CORS) https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS 里面談到了,在某些情況下,瀏覽器在發(fā)起“需要預檢的請求”之前,必須首先發(fā)起一個“預檢請求(Preflight)”到服務器,以探測服務器是否允許這個實際請求。"預檢請求"機制的使用,是為了避免跨域請求對服務器的用戶數(shù)據(jù)產(chǎn)生未預期的影響。 那么哪些請求算作“需要預檢的請求”呢?Mozilla的這篇文檔定義得很清楚: 當請求滿足下述任一條件時,即應首先發(fā)送預檢請求:
我再檢查我的代碼,因為我在HTTP請求里用xhr.setRequestHeader("Authorization", "用戶名:密碼的base64編碼" )添加了用于Basic Authentication的頭部,因此迫使該請求成為了“需要預檢的請求”,所以才有了OPTION請求的發(fā)送。 現(xiàn)在我將其注釋掉: 這次遇到了401 Unauthorized錯誤了: 然而沒有預檢請求OPTION發(fā)出來了,請求類型變成了我期望的POST方式了。 但是現(xiàn)在就陷入了一個矛盾的境地:如果在請求頭部加上Basic Authentication的信息,會遇到錯誤消息“Request header field Authorization is not allowed by Access-Control-Allow-Headers in preflight response.”。如果去掉,雖然避免了預檢請求,但是又遇到401 Unauthorized錯誤了。 于是,我換了一種認證方式,終于成功實現(xiàn)了期望的跨域請求,在我域名a的前端應用里打印出了來自于域名b的服務的響應。 我使用了form認證方式,這種方式不會造成該請求成為一個”需要預檢的請求“,所以最后跨域成功了。
希望我的這個踩坑經(jīng)歷對大家有點幫助。 要獲取更多Jerry的原創(chuàng)技術文章,請關注公眾號"汪子熙" |
|