前後端分離來說,跨域請求是我們第一個需要解決的問題。下面是我在開發中總結出來的一些經驗。
開發中,很多時候會出現Options請求(CORS預檢請求),但是有的時候又不會出現。某些請求不會觸發 CORS 預檢請求,這樣的請求被稱為簡單請求,其他的請求被稱為非簡單請求。首先我們來區分簡單請求和非簡單請求。
一、簡單請求和非簡單請求
瀏覽器將CORS請求分成兩類:簡單請求(simple request)和非簡單請求(not-so-simple request)。
只要同時滿足以下兩大條件,就屬於簡單請求。
1、請求方法是以下三種方法之一:
HEAD、GET、POST
2、HTTP的頭資訊不超出以下幾種欄位:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限於三個值application/x-www-form-urlencoded、multipart/form-data、text/plain凡是不同時滿足上面兩個條件,就屬於非簡單請求
。瀏覽器對這兩種請求的處理,是不一樣的。
二、和跨域有關的頭部梳理
現在我們知道我們哪些請求是簡單請求哪些是非簡單請求,上面有提到HTTP頭部和CORS跨域請求,CORS全稱Cross-origin resource sharing,也就是跨域資源共享,瀏覽器出於安全形度考慮限制跨域HTTP請求,這就是同源策略,所以後端需要設定請求頭部才能允許跨域。
1、Access-Control-Allow-Methods:
是逗號分隔的一個字串,表明伺服器允許的跨域請求的方法。
2、Access-Control-Allow-Headers:
是一個逗號分隔的字串,表明伺服器支援的所有頭資訊欄位,不限於瀏覽器在”預檢”中請求的欄位。
3、Access-Control-Allow-Credentials:
該欄位表示是否可以將對請求的響應暴露給頁面,一般來說是cookie。Credentials必須在前後端都被配置才能使帶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');
否則瀏覽器就會報錯:
需要注意的是,如果要傳送Cookie,Access-Control-Allow-Origin就不能設為星號,必須指定明確的、與請求網頁一致的域名。
同時,Cookie依然遵循同源政策,只有用伺服器域名設定的Cookie才會上傳,其他域名的Cookie並不會上傳,且(跨源)原網頁程式碼中的document.cookie也無法讀取伺服器域名下的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/