[譯]一篇文章解決跨域

原文地址 Using CORSjavascript

部份內容我精簡了部份內容, 若是想看所有能夠去上面的原文地址查看html

同時也附上腦圖前端

我如今也弄不清楚這篇文章算不算是翻譯html5

前言

Cross-Origin Resource Sharing(CORS) 是W3C爲瀏覽器制定的能夠跨域通訊的規範. 經過使用 XMLHttpRequest 對象, CORS可讓開發者方便的進行跨域通訊, 就像在使用同域通訊同樣.java

CORS的使用十分簡單. 想象一下有一個網站 a.com 想要獲取另外一個網站 b.com 的數據. 但因爲瀏覽器的同源策略, 這樣的請求將會被禁止. 這時咱們可使用CORS, 經過添加一些特殊的請求響應頭, 可讓 a.com 訪問 b.com 的數據.git

經過上面的例子咱們能夠看出, CORS的支持須要客戶端和服務器同時支持才行. 幸運的是, 若是你是一名客戶端的開發人員(如前端工程師), 絕大多數的技術細節都會被隱藏掉.github

這篇文章將講述客戶端如何發送一個跨域請求, 而服務器又將如何去處理和支持跨域請求.跨域

發送一個跨域請求

時至今日, 發送一個XMLHttprequest請求已是一個簡單的事情, 這裏我不在過多贅述.瀏覽器

根據瀏覽器的同源策略, 當請求的地址與來源地址的協議域名端口中的任一值不相同時, 均視爲是一個跨域的請求.緩存

XMLHttprequest 的 withCredentials 屬性

跨域請求一般不會攜帶cookies信息. 爲了能讓跨域請求帶上cookies, 你須要將作以下設置:

xhr.withCredentials = true;

爲了能讓這個屬性正常工做, 你還須要在服務器端在響應是帶上Access-Control-Allow-Credentials , 同時它的值必須爲true. 更多的內容能夠看服務器設置的那一部分.

Access-Control-Allow-Credentials: true

設置withCredentials爲true後, 在於服務器進行通訊時會攜帶這個域名下的全部cookies, 同時服務器也能夠在它的於域名下設置cookies. 但值得注意的是, 這些cookies仍然遵照瀏覽器的同源策略, 你沒法經過javascript訪問這個域名下的cookies, 它只被這個域名的服務器控制.

爲服務器增長跨域請求的支持

大部分跨域請求的重要操做實在瀏覽器和服務器之間進行的. 瀏覽器在跨域請求期間會表明客戶端在請求上增長行的請求頭, 有的時候還會增長新的請求. 這些操做對於開發者來講是透明, 可是這些請求仍然能夠被抓包工具捕獲.

跨域請求

瀏覽器的開發者來實現瀏覽器端的跨域請求細節, 這節內容來介紹如何配置服務器來支持跨域請求.

跨域請求的分類

一般將跨越請求分爲"簡單請求"和"非簡單請求"兩類.

簡單請求遵循如下的規則

簡單請求

首先它的請求方式只能是GET, POST, PUT

同時的它的請求頭部只能包含上面的那幾個類型, 值的注意的是Content-Type只能是羅列出的三種(正好是form的entryType的三個值).

對於簡單請求, 瀏覽器能夠自行解決其中的跨域問題. 例如咱們熟知的一個跨域通訊的解決方式JSON-P就是利用GET發送一個簡單請求來規避跨域的問題.HTML中的表單提交也不須要處理跨域問題.

任何不符合上述條件的請求都算做非簡單請求, 瀏覽器在處理非簡單的跨域請求時會與服務器進行額外的通訊(稱之爲預檢請求), 將在下面介紹.

處理簡單請求

經過這個cors-demo咱們能夠方便的查看瀏覽器與服務器之間的同信.

當瀏覽器發送一個簡單請求時, 咱們打開瀏覽器的Network面板能夠看到一個以下的請求(刪除部份內容)

GET /get HTTP/1.1
Host: localhost:5051
Origin: http://localhost:8080
User-Agent: Mozilla/5.0 ....
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,en;q=0.8,zh;q=0.6,ja;q=0.4,zh-TW;q=0.2

咱們須要注意的一點是, 全部的跨域請求(不管簡單或者非簡單)總會包含一個Origin的請求頭部, 這個屬性的值由瀏覽器添加, 並且不受用戶控制. 它的值由協議(如: http), 域名(如: a.om)和端口(只有它不是默認值時才包含, 如80端口)組成, 說明請求的來源.

包含Origin的請求不必定是跨域請求, 可是跨域請求必定包含Origin. 一些同源的請求一樣也會包含Origin請求頭.例如, Firefox瀏覽器不會在同源的請求中添加Origin, 可是Chrome和Safari會在同源的POST/PUT/DELETE請求中添加Origin請求頭(可是同源的GET不會添加).

瀏覽器會忽略掉同源請求中的的CORS響應中的設置.

而後咱們來看一個有效的跨域請求響應

Access-Control-Allow-Origin: http://localhost:8080
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8

全部與跨域請求相關的HTTP頭部都以Access-Control-開始, 下面是它們的詳細信息

  1. Access-Control-Allow-Origin (必選)

全部有效的跨域響應都必須包含這個請求頭, 沒有的話會致使跨域請求失敗. 它的值能夠是請求中的Origin的值, 也能夠設置爲*來表示能夠響應全部來源的請求.

  1. Access-Control-Allow-Credentials (可選)

默認狀況下跨域請求不會攜帶cookies信息. 若是須要請求攜帶cookies信息, 則須要將這個值設置爲true, 若是不須要就不要設置這個值, 而不是將它設置爲false.

這個請求頭須要與 withCredentials 配合使用. 只有兩個值都設置爲true的時候纔可以在請求中攜帶cookies信息. 當withCredentials設置爲true, 而響應中不包含Access-Control-Allow-Credentials時, 請求會發生錯誤.

  1. Access-Control-Expose-Headers (可選)

XMLHttpRequest2對象上的getResponseHeader()方法可讓你獲取到響應中頭部信息, 但在跨域請求中,你只能獲取到如下信息

  • Cache-Control

  • Content-Language

  • Content-Type

  • Expires

  • Last-Modified

  • Pragma

若是你但願客戶端能過獲取其餘的頭部信息, 能夠設置這個值.

處理一個非簡單請求

對與開發者來講, 發送一個跨域的非簡單請求跟發送一個同域請求沒什麼區別.但事實上瀏覽器會發送兩個請求, 第一個請求(成爲預檢請求)會像服務器肯定是否接受這個跨域請求, 第二個纔是真正的發出請求. 瀏覽器自動的處理這兩個請求, 同時預檢請求也是能夠被緩存的, 而不用每次請求都須要發送預檢請求.

下面是一個預檢請求

OPTIONS /cors/post HTTP/1.1
Host: localhost:5051
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type
Origin: http://localhost:8080
User-Agent: Mozilla/5.0 ...
DNT: 1
Referer: http://localhost:8080/
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,en;q=0.8,zh;q=0.6,ja;q=0.4,zh-TW;q=0.2

如同簡單跨域請求同樣, 在預檢請求中也包含了Origin請求頭, 同時這個請求的方式OPTIONS(因此你必須肯定你的服務器可以正常的處理這中請求). 它同時也包含了其餘的請求頭.

  1. Access-Control-Request-Method

這個請求頭的值就是正式請求的請求方式, 上面的那個例子就是POST

  1. Access-Control-Request-Headers

它的值是一個由逗號分隔的正式請求中請求頭的列表.

預檢請求是在實際的請求發出前先向服務器確認是否可以處理這個請求. 服務器應該檢查上邊兩個請求頭的值, 來判斷這個請求是否有效.

若是服務器確認這個請求有效, 那麼它會作出以下的響應

HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://localhost:8080
Access-Control-Allow-Headers: Content-Type
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Content-Type: text/html
Date: Sat, 12 Aug 2017 13:46:08 GMT
Connection: keep-alive
Transfer-Encoding: chunked
  1. Access-Control-Allow-Origin (必選)

如同簡單跨域請求同樣, 預檢請求的響應也必須包含這個值

  1. Access-Control-Allow-Methods (必選)

由逗號分隔的HTTP請求方式, 在其中的值表示服務可以接受這中請求方式的跨域請求.

值得注意的是雖然預檢請求只是針對單個請求方式進行檢測, 可是你仍能夠返回一個你所支持的請求方式列表.這樣作的好處是方便對預檢請求進行緩存.

  1. Access-Control-Allow-Headers (當預檢請求中包含Access-Control-Request-Headers時是必須的)

由逗號分隔的支持的請求頭部列表, 與Access-Control-Allow-Methods相似, 雖然預檢請求中只有不多的一部分請求頭, 可是你仍然能夠返回全部你支持的列表, 緣由也是爲了緩存.

  1. Access-Control-Allow-Credentials (可選)

同簡單請求

  1. Access-Control-Max-Age (可選)

在每一個請求前面都發送一個預檢請求是很浪費資源的, 這個值容許你設置預檢請求的緩存時間, 單位是秒.

一旦預檢請求經過服務器的檢查, 那麼瀏覽器會隨後發送實際的請求. 實際請求的處理與簡單請求同樣.

若是服務器想要拒絕一個跨域請求, 那麼他能夠直接回復一個簡單的響應(如 HTTP 200), 但在響應頭中不要包含任何與CORS相關的響應頭設置. 服務器也可能會由於預檢請求不合法而拒絕這個請求. 若是預檢請求中不包含正確的CORS頭部設置, 它就不會發送實際的請求.

當跨域請求發生錯時, 瀏覽器會調用onerror事件, 同時會在控制檯打印相關的錯誤信息.

cors request error

最後附上一個服務器端處理跨域請求的流程圖

服務器處理跨域請求流程圖

相關文章
相關標籤/搜索