1、爲何會有跨域問題
3、簡單請求和非簡單請求
Ajax 按照條件能夠分爲兩種請求方式,簡單請求和非簡單請求。nginx
同時知足如下兩個條件,就屬於簡單請求
ajax
- 使用下列方法之一
- 請求的header是
- Accept
- Accept-Language
- Content-Language
- Content-Type 只限於三個值
- application/x-www-form-urlencodes
- multipart/form-data
- text/plain
對於簡單請求,瀏覽器直接發出CORS請求。在頭部字段中,增長一個Origin字段。(chrome有時會隱藏這個字段)
chrome
下面是一個項目中的ajax封裝,使用的就是簡單請求:json


4、CORS請求相關的字段,都以 Access-Control- 開頭
- Access-Control-Allow-Origin :必選
- (一個或者多個容許跨越的)請求頭Origin字段的值
- *:接受任何域名
- Access-Control-Allow-Credentials: 可選
- true:表示容許發送cookie,此時 Access-Control-Allow-Origin 不能設置爲*,必須指定明確的,與請求網頁一致的域名;
- 不設置該字段,不須要瀏覽器發送cookie
- Access-Control-Expose-Headers:可選
- 列出了哪些首部能夠做爲響應的一部分暴露給外部
- 默認,只有六種暴露給外部
- Cache-Control
- Content-Language
- Content-Type
- Expires
- Last-Modified
- Pragma
- 若是想讓客戶端能夠訪問到其餘的首部信息,能夠將他們在Access-Control-Expose-Headers裏面列出來
5、withCredentials 屬性
- CORS默認不發送Cookie和HTTP認證信息,若是要把Cookie 發送到服務器,一方面須要服務器贊成,設置響應頭 Access-Control-Allow-Credentials:true;另外一方面,客戶端發送請求時,也要進行一些設置,例如 xhr.withCredentials = true;
6、非簡單請求
非簡單請求就是那種對服務器有特殊要求的請求,好比請求方法爲PUT 或者 DELETE,或者 Content-Type 爲 application/json;跨域
預檢請求和迴應
非簡單請求的CORS請求,會在正式通訊以前,增長一次HTTP查詢請求,稱爲「預檢」。瀏覽器會詢問服務器,當前網頁所在的域名是否在服務器的許可名單以內,以及可使用哪些 HTTP 動詞和頭部信息段,只有獲得確定答覆,纔會發出正式的接口請求。不然,報錯。瀏覽器
一、預檢請求服務器
- 用 OPTIONS 方法,詢問。預檢請求包括三個字段
- Origin,表示請求來自哪一個域;
- Access-Control-Request-Method:必須,瀏覽器會使用的請求方法;
- Access-Control-Request-Headers: 瀏覽器發送 CORS 請求會額外發送的頭部信息段;
二、預檢迴應cookie
- 服務器收到預檢請求後,檢查了Origin,Access-Control-Request-Method,Access-Control-Request-Headers字段後,確認容許跨域,就能夠作出迴應
- 若是瀏覽器否定了「預檢」請求,會返回一個正常的HTTP迴應,可是沒有任何CORS相關的頭部信息字段,瀏覽器會認爲不一樣意,觸發一個錯誤
- 服務器迴應的其餘CORS字段
- Access-Control-Allow-Methods: 必需,逗號分隔的字符串,表示服務器支持的全部跨域請求方法;
- Access-Control-Allow-Headers:瀏覽器支持的全部頭部字段;
- Access-Control-Allow-Credentials:Cookie
- Access-Control-Allow-Max-Age: 指定本次請求的有效期;
正常請求和迴應
一旦經過預檢請求,之後就跟簡單請求同樣。app
另外一個項目使用了這種方式,如下是其截圖:工具



7、服務端如何設置CORS
- 若是設置請求頭 Content-Type:application/x-www-form-urlencoded,則爲簡單請求
- 直接設置響應頭 Access-Control-Allow-Origin 爲 *,或者具體的域名;
- 若是響應頭Access-Control-Allow-Credentials 爲 true,則此時Access-Control-Allow-Origin 不能設置爲*,必須指定明確的域名;
- 若是設置請求頭 Content-Type:application/json,則爲非簡單請求
- 處理 OPTIONS 請求,服務端能夠單獨寫一個路由
- 能夠把這部分抽離處理,做爲一箇中間件,例如Koa;