前端CORS請求梳理

shellteo發表於2018-04-14

前後端分離來說,跨域請求是我們第一個需要解決的問題。下面是我在開發中總結出來的一些經驗。

開發中,很多時候會出現Options請求CORS預檢請求),但是有的時候又不會出現。某些請求不會觸發 CORS 預檢請求,這樣的請求被稱為簡單請求,其他的請求被稱為非簡單請求。首先我們來區分簡單請求和非簡單請求。

一、簡單請求和非簡單請求

瀏覽器將CORS請求分成兩類:簡單請求(simple request)和非簡單請求(not-so-simple request)。
只要同時滿足以下兩大條件,就屬於簡單請求。

1、請求方法是以下三種方法之一:

HEADGETPOST

2、HTTP的頭資訊不超出以下幾種欄位:

Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限於三個值application/x-www-form-urlencodedmultipart/form-datatext/plain
凡是不同時滿足上面兩個條件,就屬於非簡單請求。瀏覽器對這兩種請求的處理,是不一樣的。

二、和跨域有關的頭部梳理

現在我們知道我們哪些請求是簡單請求哪些是非簡單請求,上面有提到HTTP頭部和CORS跨域請求,CORS全稱Cross-origin resource sharing,也就是跨域資源共享,瀏覽器出於安全形度考慮限制跨域HTTP請求,這就是同源策略,所以後端需要設定請求頭部才能允許跨域。

1、Access-Control-Allow-Methods:

是逗號分隔的一個字串,表明伺服器允許的跨域請求的方法

2、Access-Control-Allow-Headers:

是一個逗號分隔的字串,表明伺服器支援的所有頭資訊欄位,不限於瀏覽器在”預檢”中請求的欄位。

3、Access-Control-Allow-Credentials:

該欄位表示是否可以將對請求的響應暴露給頁面,一般來說是cookieCredentials必須在前後端都被配置才能使帶credentials的CORS請求成功jQuery可以通過設定xhrFields: {withCredentials:true}promise可以通過設定credentials: 'include'

4、Access-Control-Max-Age:

用來指定本次預檢請求的有效期,單位為秒。

5、Access-Control-Allow-Origin:

表示資源是否被允許與給定的origin共享

6、Access-Control-Expose-Headers:

響應首部 Access-Control-Expose-Headers 列出了哪些首部可以作為響應的一部分暴露給外部。預設情況下,只有六種 simple response headers (簡單響應首部)可以暴露給外部:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想要讓客戶端可以訪問到其他的首部資訊,可以將它們在 Access-Control-Expose-Headers 裡面列出來。

7、Access-Control-Request-Headers:

請求首部 Access-Control-Request-Headers 出現於 preflight request (預檢請求)中,用於通知伺服器在真正的請求中會採用哪些請求首部

8、Access-Control-Request-Method:

請求首部 Access-Control-Request-Method 出現於 preflight request (預檢請求)中,用於通知伺服器在真正的請求中會採用哪種 HTTP 方法。因為預檢請求所使用的方法總是 OPTIONS ,與實際請求所使用的方法不一樣,所以這個首部是必要的。

三、傳送cookie

如果需要傳送cookie,前後端都被配置,前端需要設定jQuery可以通過設定xhrFields: {withCredentials:true},promise可以通過設定credentials: 'include'
伺服器端需要設定res.setHeader('Access-Control-Allow-Credentials', 'true');否則瀏覽器就會報錯:

Response to preflight request doesn’t pass access control check: The value of the ‘Access-Control-Allow-Credentials’ header in the response is ‘’ which must be ‘true’ when the request’s credentials mode is ‘include’.

需要注意的是,如果要傳送Cookie,Access-Control-Allow-Origin就不能設為星號,必須指定明確的、與請求網頁一致的域名。

同時,Cookie依然遵循同源政策,只有用伺服器域名設定的Cookie才會上傳,其他域名的Cookie並不會上傳,且(跨源)原網頁程式碼中的document.cookie也無法讀取伺服器域名下的Cookie。
這時候瀏覽器會報錯

The value of the ‘Access-Control-Allow-Origin’ header in the response must not be the wildcard ‘
‘ when the request’s credentials mode is ‘include’.

*我們來看看node如何不使用如何外掛和框架來解析cookie
,下面是程式碼:

//解析cookie的函式

function parseCookies (request) {    
    let list = {},
    rc = request.headers.cookie;

    rc && rc.split(';').forEach(function( cookie ) {        
        let parts = cookie.split('=');
        list[parts.shift().trim()] = decodeURI(parts.join('='));
    });    
    return list;
}
http.createServer(function (request, response) {    
    //讀取cookie
    var cookies = parseCookies(request);
    //寫入cookie
    response.writeHead(200, {            
        'Set-Cookie': 'mycookie=test',            
        'Content-Type': 'text/plain'
    });
    response.end('Hello World\n');}).listen(8124);
})
複製程式碼

四、Content-Type的值及其作用

對接後端的介面的時候,Content-Type的值是決定後端那邊怎麼去解析我們的傳參的。Content-type常用幾個值:

1.text/html
2.text/plain
3.text/css
4.text/javascript
5.application/x-www-form-urlencoded
6.multipart/form-data
7.application/json
8.application/xml

前面幾個都很好理解,都是html,css,javascript的檔案型別,後面四個是POST的發包方式

1、application/x-www-form-urlencoded

html中表單提交的預設格式,jquery ajax請求預設的content-type預設也是這種格式,並且jquery會把data:{foo1:”bar1”,foo2:”bar2”}資料轉換成key1=value1&key2=value2,可以通過processData來關閉是否提前處理資料,資料格式是key1=value1&key2=value2形式。

下面是jquery做data預處理的程式碼,預設processData為true,所以如果data有值就會進入判斷就會把json格式轉化為key1=value1&key2=value2的格式

if ( s.data && ( s.processData || typeof s.data === "string" ) ) {
    cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data;
    
    // #9682: remove data so that it's not used in an eventual retry
    delete s.data;
}複製程式碼

2、multipart/form-data

multipart/form-data用在傳送檔案的POST包。
這裡Content-Type告訴我們,發包是以multipart/form-data格式來傳輸,另外,還有boundary用於分割資料。
當檔案太長,HTTP無法在一個包之內傳送完畢,就需要分割資料,分割成一個一個chunk傳送給服務端,
那麼—用於區分資料快,而後面的資料sqe6Nhq4gtMfHLOY 就是標示區分包作用。

------WebKitFormBoundarysqe6Nhq4gtMfHLOY
Content-Disposition: form-data; name:"UserWxCode"

123
------WebKitFormBoundarysqe6Nhq4gtMfHLOY
Content-Disposition: form-data; name="CodeType"

Public
------WebKitFormBoundarysqe6Nhq4gtMfHLOY--複製程式碼

3、application/json

傳送請求的時候需要JSON.stringify一下,HTTP通訊中並不存在所謂的json,而是將string轉成json罷了,也就是,application/json可以將它理解為text/plain,普通字串

4、text/xml和application/xml

text/xml忽略xml檔案頭中的關於編碼的設定(<?xml version=”1.0” encoding=”UTF-8”?>),預設採用us-ascii編碼。 application/xml會依照xml檔案頭中編碼的設定推薦使用application/xml

五、fetch和jquery Ajax設定Headers,cookies

//fetch設定Headers,cookies

//方式1
let myHeaders = new Headers({    
   'Content-Type': 'application/json; charset=UTF-8',    
   'token': 123
});

//方式2
headers: {    
   'Content-Type': 'application/json; charset=UTF-8',    
   'token': 123
}

//傳送cookies:
credentials: 'include'

//jquery ajax設定Headers,cookies

//設定header方式一
headers: {
   UserName: 'zxplus',
   EncryptKey: '1111'
},

//方式二
beforeSend: function(request) {
    request.setRequestHeader("UserName", "zxplus");
},
//傳送cookie
xhrFields: {
   withCredentials:true //支援附帶詳細資訊
},複製程式碼

參考

https://stackoverflow.com/questions/3393854/get-and-set-a-single-cookie-with-node-js-http-server
http://www.ruanyifeng.com/blog/2016/04/cors.html
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS
https://segmentfault.com/a/1190000006039533
http://www.cnblogs.com/caoshiqing/p/6825608.html
http://homeway.me/2015/07/19/understand-http-about-content-type/


相關文章