credit:https://medium.com/@baphemot前端
譯自:https://medium.com/@baphemot/understanding-cors-18ad6b478e2bnode
「呃。。還行, 但不夠」
webpack
若是你常常跟AJAX call打交道,那麼你確定遇到過下面這個錯誤。web
若是你看到這條消息,意味着響應失敗了,但你仍是能在Console裏的Network標籤下,看到返回的數據。chrome
那麼,這裏究竟是怎麼一回事呢?express
你所遇到的這種行爲就是瀏覽器跨域的實現。npm
考慮到安全問題,在跨域標準化以前,若是你想調用一個節點在不一樣域的API, 是不存在的。這種調用,會由於 Same-Origin 政策被阻止。後端
設計CORS這種機制是爲了,第一,使你所發出的請求能表明你自身; 第二, 阻止那些流氓JS發出的請求; 第三,這種機制會被激活,不管什麼時候你發送請求到:api
1). 不一樣的域名。(eg. 應用在 example.com 調用 api.com)跨域
2). 不一樣的子域名。(eg. 應用在 example.com 調用 api.com)
3). 不一樣的端口。(eg. 應用在 example.com 調用 example.com:3001)
4). 不一樣的協議。(eg. 應用在 example.com 調用 example.com)
經過這種機制,咱們可以阻止黑客的腳本攻擊,以防當你登錄,好比銀行網站,的時候替換你的驗證信息。
若是你的瀏覽器嘗試發起一個‘不簡單’的請求(好比: 一個請求包含了cookies, 或者 Content-type
不是application/x-ww-form-urlencoded
, multipart/form-data
或者 text-plain
)這時候會調用有一種叫作 預檢(preflight)的機制,而後會發送一個options
請求到服務器。若是服務器的響應,沒有攜帶特定的headers, 隨後的‘簡單‘get
或post
請求仍是會發送,可是瀏覽器不會容許JS去訪問的收到的數據。
若是你明確想要添加cookies,自定義headers和其餘一些features,那這個請求就再也不是一個‘簡單’請求。那麼服務器就不會合理地響應,請求也不會被髮送。
CORS使用不多的HTTP請求頭(在請求和響應裏都是),可是有一點你必須明白,並且有能力去在工做中應用:
這個請求頭通常會被服務器端返回,它的值表明了哪些域名你有權能夠訪問。 它的值能夠爲:
*
容許訪問任何域若是你請求客戶端傳一些用做驗證的請求頭,好比cookies, 那麼你就不能將Access-Control-Allow-Origin的值設定爲*
—必須是安全驗證過的域名才能夠!
若是一個服務器支持經過cookies來驗證,那麼必需要在響應裏帶上這個請求頭。
True是其惟一有有效的值。
一個逗號分隔的list,存放表明服務器願意支持的請求頭。(eg. 好比x-authentication-token
, 你須要將其包含在ACA header裏, 返回給前面提到的options
請求, 不然你的請求會被blocked)
跟上面類似,這個請求頭包含一系列用戶可用的headers,這些headers會出如今真實響應裏,並且客戶端是可使用的。其餘的全部header會被blocked。
這個比較簡單,存放全部服務端支持的HTTP請求類型(好比get
,post
)。
客戶端請求頭的一部分,其值包含客戶端app啓動處的域名。 出於安全考慮,瀏覽器將不容許你去重寫這個值。
你不得不認可CORS並非一種‘錯誤’。它是一種預期的機制爲了去保護用戶,你,還有你發送請求的目標網站。
有時候缺少合理的請求頭是客戶端的一種錯誤的行爲(eg. 缺乏驗證信息好比API key)。
這裏我將給你一些方法去「解決錯誤」,選擇哪一種方法,這取決於你所應用的場景:
A - 我開發前端,後端我認識,聽個人 ;)
嗯這固然是最好的狀況, 你就能夠去實現合理的CORS響應在你所請求的服務器端。若是一個API正在使用node的express
框架,你只要用一下cors
的包就好了。若是你想使你的網站更加合理安全,你可能要考慮使用一個白名單給Access-Control-Allow-Origin
請求頭。
B - 我開發前端,後端我不熟,暫時須要一個臨時的解決方案 :)
這是第二好的狀況,由於這就是A狀況,只不過有一些時間限制。若是你想臨時解決這個問題,你可讓你的瀏覽器忽略CORS機制,舉個栗子,使用ACAO Chrome插件,或者在用Chome的時候跑一下下面的flags:
chrome --disable-web-security --user-data-dir複製代碼
重要:請記住這條命令會應用於全部網站,而且存在於整個瀏覽器會話中。請當心使用。
另外一個路子就是,你可使用devServer.proxy(假設你使用webpack去serve你的app)或者使用一個 CORS-as-a-service 解決方案,好比cors-anywhere.herokuapp.com/
C - 我開發前端,我想要調後端? 不存在的 :`(
好吧,如今事情就變得複雜了。首先,你應該可能須要搞清,爲何服務器端沒有發送一個正確的請求頭。
可能它們不容許使用第三方的庫的app去訪問?可能它們的API只給服務器端的應用使用, 而不是瀏覽器?可能你在請求時沒有發送用於驗證的token?
若是你仍然認爲你有可以經過瀏覽器獲得數據,你應該去寫一個本身的代理,存在於瀏覽器應用和API之間,就像咱們在方案B中所作的同樣。
Adding a proxy in the middle
這個代理服務器,不是必須和你的應用跑在相同的域上。只要使得這個代理服務器,在與客戶端交流時支持CORS就能夠。在與API交流時不是必需要支持CORS。
你能夠寫一個本身的平臺,或者使用一個已有的解決方案,好比
記住,這種方法可能存在安全風險,若是你想要支持驗證的話。
若是你想學更多關於CORS的細節,我推薦你去查看更加細節化的MDN article.