axios源碼解析 – 請求攔截器

  • 2022 年 5 月 11 日
  • 筆記

axios請求攔截器,也就是在請求發送之前執行自定義的函數。

axios源碼版本 – ^0.27.2 (源碼是精簡版)

平時在業務中會這樣去寫請求攔截器,程式碼如下:

// 創建一個新的實例
var service = axios.create();

// 請求攔截器
service.interceptors.request.use((config) => {
  // 請求頭加token
  config.headers['token'] = 'xxx';
  ... ... ...
 
  return config;
}, (err) => {
  return Promise.reject(err);
});

 其中 service.interceptors.request.use 方法起到了作用,其核心源碼如下:

/* axios/core/Axios.js */
// Axios構造函數
function Axios(defaultConfig) {
  this.defaults = defaultConfig;
  this.interceptors = {
    request: new InterceptorManager(),
    response: new InterceptorManager()
  }
}

/* axios/core/InterceptorManager.js */
// InterceptorManager構造函數
function InterceptorManager() {
  // 默認空數組
  this.handlers= [];
}

// InterceptorManager原型上定義use方法 (上述業務中的use方法,其實就是調用了該方法)
InterceptorManager.prototype.use = function(fulfilled, rejected) {
    // 數組中push一個包含成功回調、失敗回調的對象
    this.handlers.push({
      fulfilled: fulfilled,
      rejected: rejected
    });
    
    // 返回數組長度 - 1
    return this.handlers.length - 1;
}

// InterceptorManager原型上定義eject方法
InterceptorManager.prototype.eject = function(id) {
  if (this.handlers[id]) {
    this.handlers[id] = null;
  }
}

// InterceptorManager原型上定義forEach方法
InterceptorManager.prototype.forEach = function(fn) {
  utils.forEach(this.handlers, function(h) {
    if (h !== null) {
      fn(h);
    }
  });  
}

service.interceptors.request.use 原來就是向handlers數組push了一個對象,可列印資訊查看,程式碼如下:

console.log(service.interceptors.request.handlers);
// 結果如下
[
  {
    fulfilled: (config) => {...},
    rejedcted: (err) => {...}
  }
]

那麼,在發送請求時,請求攔截器是如何運作的,程式碼如下:

Axios.prototype.request = function(defaultConfigOrUrl, config) {
  ... ... ...

  var requestInterceptorChain = [];
  this.interceptors.request.forEach(function (interceptor) {
    // 將該實例的this.this.interceptors.request.handlers放入requestChain 
    requestInterceptorChain.unshift(interceptor.fulfilled, interceptor.rejected);
  });

  var promise;

  // dispatchRequest就是請求過程
  var chain = [dispatchRequest, undefined];
  Array.prototype.unshift.apply(chain, requestInterceptorChain);

  promise = Promise.resolve(config);
  while (chain.length) {
    // 鏈式調用,不斷減少chain數組的長度,直至為空
    promise = promise.then(chain.shift(), chain.shift());
  }

  return promise;

  ... ... ...   
}

這也便是說為什麼axios是基於promise的,請求攔截器 –> 請求 –> 響應攔截器。

 

取消攔截器

取消攔截器功能,作者覺得作用不大,程式碼如下:

var requestNumber = service.interceptors.request.use(fulfilled, rejected);

// 取消請求攔截器
service.interceptors.request.reject(requestNumber);

service.post({})

 

多個請求攔截器

主要講多個請求攔截器的執行順序,程式碼如下:

// 第一個請求攔截器
service.interceptors.request.use(fulfilled1, rejected1);

// 第二個請求攔截器
service.interceptors.request.use(fulfilled2, rejected2);

// 第三個請求攔截器
service.interceptors.request.use(fulfilled3, rejected3);

// 根據上述Axios.prototype.request方法,可以知道handlers數組:[fulfilled3, rejected3, fulfilled2, rejected2, fulfilled1, rejected1]

// 所以執行順序:fulfilled3 --> fulfilled2 --> fulfilled1