1. Interceptors 攔截器
axios 官網中對Interceptors
的使用方法如下: 用戶可以通過 then 方法為請求添加回調,而攔截器中的回調將在 then 中的回調之前執(zhí)行:
// Add a request interceptor
axios.interceptors.request.use(function (config) {
// Do something before request is sent
return config;
}, function (error) {
// Do something with request error
return Promise.reject(error);
});
// Add a response interceptor
axios.interceptors.response.use(function (response) {
// Do something with response data
return response;
}, function (error) {
// Do something with response error
return Promise.reject(error);
});
之后你可能需要能移除 Interceptors :
const myInterceptor = axios.interceptors.request.use(function () {/*...*/});
axios.interceptors.request.eject(myInterceptor);
也可以為axios實例添加一個Interceptors:
const instance = axios.create();
instance.interceptors.request.use(function () {/*...*/});
從其中的使用方法庆械,我們可以知道 request 攔截器需要在請求之前執(zhí)行盲憎,response 攔截器需要再請求之后執(zhí)行亏较。我們看一下axios的實現(xiàn)方式: 首先axios為攔截器定義了一個管理中心InterceptorManager:
function InterceptorManager() {
this.handlers = [];
}
InterceptorManager.prototype.use = function use(fulfilled, rejected) {
this.handlers.push({
fulfilled: fulfilled,
rejected: rejected
});
return this.handlers.length - 1;
};
InterceptorManager.prototype.forEach = function forEach(fn) {
utils.forEach(this.handlers, function forEachHandler(h) {
if (h !== null) {
fn(h);
}
});
};
2.當實例化Axios時,分別創(chuàng)建一個request
和一個response
攔截器:
function Axios(instanceConfig) {
this.defaults = instanceConfig;
this.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager()
};
}
然后我們需要通過use來分別添加攔截器時,便將我們定義的resolve和reject收入對應的request和response中。在此之前讹蘑,我們需要對Promise有一個簡單的了解:
Promise then 方法返回的是一個新的 Promise 實例(注意,不是原來那個 Promise 實例)筑舅。因此可以采用鏈式寫法座慰,即 then 方法后面再調用另一個 then 方法。
getJSON("/posts.json").then(function(json) {
return json.post;
}).then(function(post) {
// ...
});
上面的代碼使用then方法翠拣,依次指定了兩個回調函數版仔。第一個回調函數完成以后,會將返回結果作為參數,傳入第二個回調函數蛮粮。采用鏈式的then益缎,可以指定一組按照次序調用的回調函數。這時然想,前一個回調函數莺奔,有可能返回的還是一個Promise對象(即有異步操作),這時后一個回調函數又沾,就會等待該Promise對象的狀態(tài)發(fā)生變化弊仪,才會被調用。
接下來杖刷,看看request方法是如何實現(xiàn)攔截器功能的:
Axios.prototype.request = function request(config) {
...
var chain = [dispatchRequest, undefined];
var promise = Promise.resolve(config);
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
chain.unshift(interceptor.fulfilled, interceptor.rejected);
});
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
chain.push(interceptor.fulfilled, interceptor.rejected);
});
while (chain.length) {
promise = promise.then(chain.shift(), chain.shift());
}
return promise;
};
首先定義了一個數組調用鏈chain,然后通過unshift將 request interceptors推入數組頭部驳癌,將response interceptors推入數組尾部滑燃。最后通過while這樣一個循環(huán)執(zhí)行promise.then來達到鏈式調用。 來看一張圖
通過巧妙的利用unshift颓鲜、push表窘、shift
等數組隊列、棧方法甜滨,實現(xiàn)了請求攔截乐严、執(zhí)行請求、響應攔截的流程設定衣摩,注意無論是請求攔截還是響應攔截昂验,越先添加的攔截器總是越“貼近”執(zhí)行請求本身。
3. 自動轉換JSON數據
在默認情況下艾扮,axios將會自動的將傳入的data對象序列化為JSON字符串既琴,將響應數據中的JSON字符串轉換為JavaScript對象。這是一個非常實用的功能泡嘴,但實現(xiàn)起來非常簡單:
var defaults = {
...
transformRequest: [function transformRequest(data, headers) {
normalizeHeaderName(headers, 'Content-Type');
if (utils.isFormData(data) ||
utils.isArrayBuffer(data) ||
utils.isBuffer(data) ||
utils.isStream(data) ||
utils.isFile(data) ||
utils.isBlob(data)
) {
return data;
}
if (utils.isArrayBufferView(data)) {
return data.buffer;
}
if (utils.isURLSearchParams(data)) {
setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded;charset=utf-8');
return data.toString();
}
// 這里對 object 做了轉換
if (utils.isObject(data)) {
setContentTypeIfUnset(headers, 'application/json;charset=utf-8');
return JSON.stringify(data);
}
return data;
}],
transformResponse: [function transformResponse(data) {
// 嘗試轉換 string -> json
if (typeof data === 'string') {
try {
data = JSON.parse(data);
} catch (e) { /* Ignore */ }
}
return data;
}]
...
}
4. 支持客戶端 XSRF 攻擊防護
XSRF 攻擊甫恩,即“跨站請求偽造”(Cross Site Request Forgery)攻擊。通過竊取用戶cookie酌予,讓用戶在本機(即擁有身份 cookie 的瀏覽器端)發(fā)起用戶所不知道的請求磺箕。防護XSRF攻擊的一種方法是設置特殊的 xsrf token,axios實現(xiàn)了對這種方法的支持:
// 設置 xsrf 的 cookie 字段名和 header 字段名
{
// `xsrfCookieName` is the name of the cookie to use as a value for xsrf token
xsrfCookieName: 'XSRF-TOKEN', // default
// `xsrfHeaderName` is the name of the http header that carries the xsrf token value
xsrfHeaderName: 'X-XSRF-TOKEN', // default
}
module.exports = function xhrAdapter(config) {
return new Promise(function dispatchXhrRequest(resolve, reject) {
...
// Add xsrf header
// This is only done if running in a standard browser environment.
// Specifically not if we're in a web worker, or react-native.
if (utils.isStandardBrowserEnv()) {
var cookies = require('./../helpers/cookies');
// Add xsrf header
// 如果允許cookie跨域或者同源抛虫,首先會從cookie中讀取定義的token
// 如果存在 xsrfValue 會將讀取到的 xsrfValue 攜帶進入 requestHeaders 中
var xsrfValue = (config.withCredentials || isURLSameOrigin(config.url)) && config.xsrfCookieName ?
cookies.read(config.xsrfCookieName) :
undefined;
if (xsrfValue) {
requestHeaders[config.xsrfHeaderName] = xsrfValue;
}
}
});
};