欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

axios攔截器機制的實現(xiàn)原理詳解

 更新時間:2024年05月06日 08:55:41   作者:zhangbao90s  
axios 不僅提供了一套跨平臺請求,另外還提供了“攔截器”這一中間件機制,方便你在請求之前、響應(yīng)之后做些操作處理,本文給大家詳細介紹了axios 攔截器機制是如何實現(xiàn)的,需要的朋友可以參考下

axios 攔截器簡介

axios 中的攔截器分“請求攔截器”和“響應(yīng)攔截器”。

請求攔截器是在請求正式發(fā)起前調(diào)用的。

// 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);
  });

請求攔截器是通過 axios.interceptors.request.use() 方法注冊的,你可以多次調(diào)用,這樣可以同時設(shè)置多個攔截器。

請求攔截器的作用允許你在請求正式發(fā)起前,對請求配置信息做最后的統(tǒng)一修改。

當(dāng)然,攔截器也支持移除。

const interceptorID = axios.interceptors.request.use(function () {/*...*/});
axios.interceptors.request.eject(interceptorID);

axios.interceptors.request.use() 會返回當(dāng)前攔截器在內(nèi)部 ID 值(一個數(shù)值),你可以通過 axios.interceptors.eject(interceptorID) 移除這個攔截器。

響應(yīng)攔截器與此同理。

// Add a response interceptor
axios.interceptors.response.use(function (response) {
    // Any status code that lie within the range of 2xx cause this function to trigger
    // Do something with response data
    return response;
  }, function (error) {
    // Any status codes that falls outside the range of 2xx cause this function to trigger
    // Do something with response error
    return Promise.reject(error);
  });

響應(yīng)攔截器是通過 axios.interceptors.response.use() 方法注冊的,你可以多次調(diào)用,這樣可以同時設(shè)置多個攔截器。

響應(yīng)攔截器不僅作用于有響應(yīng)的返回(比如 2xx、 4xx、5xx),對于像網(wǎng)絡(luò)請求意外中斷(比如超時)、跨域請求錯誤(CORS)這樣沒有響應(yīng)的請求同樣適用。

響應(yīng)攔截器的作用是允許你在響應(yīng)在給用戶處理之前,先對響應(yīng)結(jié)果(比如 reponse.data)做統(tǒng)一處理。

同時,你可以通過 interceptors.response.eject(interceptorID) 移除特定的響應(yīng)攔截器。

Axios 實例

你可以將 axios 庫暴露出來的 axios 對象近似看成是內(nèi)部 Axios 類的實例。

Axios 類實例在創(chuàng)建的時候,會掛載一個對象屬性 interceptors,這個對象又包含 request 和 response 2 個屬性,它們都是 InterceptorManager 類的實例對象。

// /v1.6.8/lib/core/Axios.js
class Axios {
  constructor(instanceConfig) {
    this.defaults = instanceConfig;
    this.interceptors = {
      request: new InterceptorManager(),
      response: new InterceptorManager()
    };
  }

攔截器類

InterceptorManager 類也就是攔截器類了,它的大概結(jié)構(gòu)如下。

// /v1.6.8/lib/core/InterceptorManager.js
class InterceptorManager {
  constructor() {
    this.handlers = [];
  }
    
  // Add a new interceptor to the stack
  use(fulfilled, rejected, options) {/* ... */}
  // Remove an interceptor from the stack
  eject(id) {/* ... */}
  // Clear all interceptors from the stack
  clear() {/* ... */}
  // Iterate over all the registered interceptors
  forEach(fn) {/* ... */}
}

InterceptorManager 內(nèi)部維護了一個攔截器數(shù)組 handlers。

實例上除了前面介紹過的 use() 和 eject() 方法,還有另外 2 個 clear()、forEach() 方法,這 4 個方法本質(zhì)上都是對內(nèi)部 handlers 數(shù)組進行操作。

先說 use()。

// /v1.6.8/lib/core/InterceptorManager.js#L18
use(fulfilled, rejected, options) {
  this.handlers.push({
    fulfilled,
    rejected,
    synchronous: options ? options.synchronous : false,
    runWhen: options ? options.runWhen : null
  });
  return this.handlers.length - 1;
}

use() 方法是用來注冊攔截器的,也就是向 handlers 數(shù)組添加一個成員。

use() 接收 3 個參數(shù),第一個參數(shù)和第二個參數(shù)分別用來定義攔截器正常操作和異常捕獲的邏輯;最后一個選項參數(shù) options 是可選的,定位攔截器行為,不太常用,為避免行文冗長,不會介紹。

use() 會返回當(dāng)前攔截器在內(nèi)部 handlers 數(shù)組的索引值,是后續(xù)用來移除攔截器的標(biāo)識(interceptorID)。

再是 eject(id)。

// /v1.6.8/lib/core/InterceptorManager.js#L35-L39
eject(id) {
  if (this.handlers[id]) {
    this.handlers[id] = null;
  }
}

就是清空內(nèi)部 handlers 數(shù)組。

最后是 forEach(fn)。

// /v1.6.8/lib/core/InterceptorManager.js#L62-L68
forEach(fn) {
  this.handlers.forEach(function forEachHandler(h) {
    if (h !== null) {
      fn(h);
    }
  });
}

用來遍歷所有攔截器,不過這里會做為空判斷,確保會忽略被移除的攔截。

到這里我們就講清楚攔截器實例了。

那么 axios 是在發(fā)請求前后是如何讓攔截器生效的呢?

攔截器實現(xiàn)原理

在上文的學(xué)習(xí)中,我們了解到 axios 請求會由內(nèi)部 axios._request() 方法處理,而在 axios._request() 方法內(nèi)部,會調(diào)用 dispatchRequest() 方法進行實際請求。

不過,dispatchRequest() 方法執(zhí)行前后,其實是有攔截器執(zhí)行邏輯的,不過之前沒說,現(xiàn)在我們就來看下。

在 axios 內(nèi)部,實際的請求會包含攔截器的執(zhí)行邏輯,以便做中間操作。

收集攔截器

在此之前,我們就要先收集攔截器。

// /v1.6.8/lib/core/Axios.js#L115-L131
const requestInterceptorChain = [];
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
  requestInterceptorChain.unshift(interceptor.fulfilled, interceptor.rejected);
});

const responseInterceptorChain = [];
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
  responseInterceptorChain.push(interceptor.fulfilled, interceptor.rejected);
});

這里使用了 2 個變量 requestInterceptorChain 和 responseInterceptorChain 來分別收集使用 axios.interceptors.request.use() 和 axios.interceptors.response.use() 注冊的攔截器。

收集的過程采用的是 interceptor 實例的 forEach() 方法,這樣就能過濾掉被移除的攔截器。

這里有 2 個點值得注意:

  • 攔截器推入 2 個 Chain 的方式是 .unshift/push(interceptor.fulfilled, interceptor.rejected) 而非 .unshift/push(interceptor),其實 2 種方式都行,不過 axios 選擇了前者
  • 另外,請求攔截器的推入方式是使用 unshift()(而非響應(yīng)攔截器的 push()),就表示后注冊的請求攔截器會先執(zhí)行,這一點需要注意
axios.interceptors.request.use(
  (config) => {
    console.log('request interceptor 1')
    return config;
  }
)

axios.interceptors.request.use(
  (config) => {
    console.log('request interceptor 2')
    return config;
  }
)

axios.get('https://httpstat.us/200')
  
// request interceptor 2
// request interceptor 1

拼接請求鏈

收集好攔截器后,就著手拼接請求鏈了。

// /v1.6.8/lib/core/Axios.js#L133-L150
let promise;
let i = 0;
let len;

const chain = [
  ...requestInterceptorChain,
  ...[dispatchRequest.bind(this), undefined],
  ...responseInterceptorChain
]
len = chain.length;

promise = Promise.resolve(config);

while (i < len) {
  promise = promise.then(chain[i++], chain[i++]);
}

return promise;

請求鏈 chain 以請求攔截器開始、實際請求居中、響應(yīng)攔截器最后的方式拼接。

整個 Promise 鏈以請求配置 config 作為起點執(zhí)行,到 dispatchRequest() 之后返回 response,因此后續(xù)響應(yīng)攔截器的入?yún)⒆兂?response 了。

當(dāng)我們以 axios.get('httpstat.us/200').then(… 請求時,對 axios 來說,完整的請求鏈?zhǔn)牵?/p>

Promise.resolve({ url: 'https://httpstat.us/200' })
  .then(
    requestInterceptorFulfilled,
    requestInterceptorRejected,
  )
  .then(
    dispatchRequest.bind(this),
  )
  .then(
    responseInterceptorFulfilled,
    responseInterceptorRejected,
  )
  .then(
    console.log
  )

由此,我們便講完了 axios 的攔截器的執(zhí)行原理以及 axios 的完整請求鏈結(jié)構(gòu)。

由請求鏈結(jié)構(gòu)看錯誤處理

明白了 axios 請求鏈結(jié)構(gòu),我們就能明白 axios 中拋錯場景的底層邏輯。

第一個請求攔截器出錯

通過前面的學(xué)習(xí),我們已經(jīng)知道請求攔截器的執(zhí)行跟注冊順序是相反的,第一個請求攔截器會最后執(zhí)行。

當(dāng)?shù)谝粋€請求攔截器出錯時,由于 dispatchRequest() 部分并沒有處理異常的部分。

.then(
  dispatchRequest.bind(this),
)

所以,錯誤會直接穿透到第一個響應(yīng)攔截器,交由對應(yīng)的 rejected 函數(shù)處理。

axios.interceptors.request.use(
  (config) => {
    console.log('request interceptor 1')
    throw new Error('Oops 1')
  }
)

axios.interceptors.response.use(
  undefined,
  (error) => {
    console.log('[error] response interceptor 1', error)
  }
)

axios.get('https://httpstat.us/200').then(console.log)

效果如下:

由于第一個響應(yīng)攔截器的 onReject 函數(shù)沒有返回值,所以 .then(console.log) 最終打印出來的時 undefined。

最后一個請求攔截器出錯

最后一個請求攔截器會最先執(zhí)行,它的錯誤會被倒數(shù)第二個請求攔截器的 rejected 函數(shù)處理。

axios.interceptors.request.use(
  undefined,
  (error) => {
    console.log('[error] request interceptor 1', error)
  }
)

axios.interceptors.request.use(
  (config) => {
    console.log('request interceptor 2')
    throw new Error('Oops 2')
  }
)

axios.interceptors.response.use(
  undefined,
  (error) => {
    console.log('[error] response interceptor 1', error)
  }
)

axios.get('https://httpstat.us/200').then(console.log)

效果如下:

最后一個請求攔截器"request interceptor 2"出錯后,會由"request interceptor 1"的 rejected 函數(shù)處理。

由于"request interceptor 1"的 rejected 函數(shù)沒有返回值,導(dǎo)致傳遞給 dispatchRequest() 實際執(zhí)行時接收到的 config 是 undefined,內(nèi)部就會報錯了。

dispatchRequest() 的錯誤會由 "response interceptor 1"的 rejected 函數(shù)處理,由于"response interceptor 1"的 rejected 函數(shù)沒有返回值,導(dǎo)致 .then(console.log) 打印結(jié)果 undefined。

axios 請求出錯

axios 請求如果出錯,會先經(jīng)過響應(yīng)攔截器處理。

axios.interceptors.response.use(
  undefined,
  (error) => {
    console.log('[error] response interceptor 1', error)
  }
)

axios.get('https://httpstat.uss/200').then(console.log)

效果如下:

我們請求了一個無效地址,結(jié)果報錯被響應(yīng)攔截器處理,由于沒有返回值,導(dǎo)致 .then(console.log) 打印結(jié)果 undefined。

響應(yīng)攔截器出錯

響應(yīng)攔截器之后就是用戶自己的處理邏輯了,如果是在響應(yīng)攔截器中出錯,那么就會錯誤就會落入用戶的 catch 處理邏輯。

axios.interceptors.response.use(
  (response) => {
    console.log('response interceptor 1')
    throw new Error('Oops 1')
  }
)

axios.get('https://httpstat.uss/200').catch(err => console.log('err >>>', err))

效果如下:

總結(jié)

axios 中提供了一個中間件機制稱為“攔截器”,分請求攔截器和響應(yīng)攔截器 2 種。請求攔截器在請求發(fā)起之前執(zhí)行,允許你修改請求配置,執(zhí)行順序與注冊順序相反;響應(yīng)攔截器在請求完成之后執(zhí)行,允許你修改響應(yīng)對象,執(zhí)行順序與注冊順序一樣。

本文帶大家由淺入深地了解了 axios 的攔截器原理,并查看了 axios 完整的請求鏈,最后基于請求鏈結(jié)構(gòu)了解 axios 中拋錯處理場景的底層邏輯。

以上就是axios攔截器機制的實現(xiàn)原理詳解的詳細內(nèi)容,更多關(guān)于axios實現(xiàn)攔截器機制的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論