axios源碼系列(三) --- 默認配置和取消請求

前言

這應該是一個大多數都經常使用的請求庫,由於它能夠支持多種配置,跨平臺實現,返回promise進行鏈式調用.徹底過一遍源碼能夠提高本身對請求庫的理解知識node

axios源碼系列(一) --- 目錄結構和工具函數
axios源碼系列(二) --- 適配器內部
axios源碼系列(三) --- 默認配置和取消請求
axios源碼系列(四) --- Axios和dispatchRequest與攔截器ios

默認配置

axios/lib/defaults.js

這個Axios庫的默認參數配置json

var utils = require('./utils');
var normalizeHeaderName = require('./helpers/normalizeHeaderName');

var DEFAULT_CONTENT_TYPE = {
  'Content-Type': 'application/x-www-form-urlencoded'
};

function setContentTypeIfUnset(headers, value) {
  if (!utils.isUndefined(headers) && utils.isUndefined(headers['Content-Type'])) {
    headers['Content-Type'] = value;
  }
}

function getDefaultAdapter() {
  var adapter;
  if (typeof XMLHttpRequest !== 'undefined') {
    // For browsers use XHR adapter
    adapter = require('./adapters/xhr');
  } else if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
    // For node use HTTP adapter
    adapter = require('./adapters/http');
  }
  return adapter;
}

這裏只有四件事:axios

  • 引入了工具庫和解析頭部名函數
  • 設置默認Content-Type爲瀏覽器默認的編碼格式
  • 定義設置headers內值爲空的Content-Type對應值的方法
  • 根據宿主環境獲取對應的adapters

裏面解析頭部名函數的源碼以下segmentfault

axios/lib/helpers/normalizeHeaderName.js

過濾得出是否不一樣大小寫字母的等價請求頭,是的話則設置新的請求頭移除舊的請求頭promise

var utils = require('../utils');

module.exports = function normalizeHeaderName(headers, normalizedName) {
  utils.forEach(headers, function processHeader(value, name) {
    if (name !== normalizedName && name.toUpperCase() === normalizedName.toUpperCase()) {
      headers[normalizedName] = value;
      delete headers[name];
    }
  });
};

這是暴露出去的對象瀏覽器

var defaults = {
  adapter: getDefaultAdapter(),

  transformRequest: [function transformRequest(data, headers) {
    normalizeHeaderName(headers, 'Accept');
    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();
    }
    if (utils.isObject(data)) {
      setContentTypeIfUnset(headers, 'application/json;charset=utf-8');
      return JSON.stringify(data);
    }
    return data;
  }],

  transformResponse: [function transformResponse(data) {
    /*eslint no-param-reassign:0*/
    if (typeof data === 'string') {
      try {
        data = JSON.parse(data);
      } catch (e) { /* Ignore */ }
    }
    return data;
  }],

  /**
   * A timeout in milliseconds to abort a request. If set to 0 (default) a
   * timeout is not created.
   */
  timeout: 0,

  xsrfCookieName: 'XSRF-TOKEN',
  xsrfHeaderName: 'X-XSRF-TOKEN',

  maxContentLength: -1,

  validateStatus: function validateStatus(status) {
    return status >= 200 && status < 300;
  }
};

defaults.headers = {
  common: {
    'Accept': 'application/json, text/plain, */*'
  }
};

utils.forEach(['delete', 'get', 'head'], function forEachMethodNoData(method) {
  defaults.headers[method] = {};
});

utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
  defaults.headers[method] = utils.merge(DEFAULT_CONTENT_TYPE);
});

module.exports = defaults;

主要提供了幾個默認配置cookie

配置 做用
adapter 宿主環境對應的適配器
transformRequest 內置轉換函數,根據對應格式設置請求
transformResponse 內置轉換函數,若是是字符型則反序列化
timeout 超時時間
xsrfCookieName 用做 xsrf token 的值的cookie的名稱
xsrfHeaderName 承載 xsrf token 的值的 HTTP 頭的名稱
maxContentLength 定義容許的響應內容的最大尺寸
validateStatus 內置對於給定的HTTP 響應狀態碼是 resolve 或 reject promise的函數
headers 默認請求頭
['delete', 'get', 'head'] 定義相關請求方式的默認請求頭{}
[post', 'put', 'patch'] 定義相關請求方式的默認請求頭,{'Content-Type': 'application/x-www-form-urlencoded'}

取消請求

axios/lib/cancel/Cancel.js

/**
 * A `Cancel` is an object that is thrown when an operation is canceled.
 *
 * @class
 * @param {string=} message The message.
 */
function Cancel(message) {
  this.message = message;
}


Cancel.prototype.toString = function toString() {
  return 'Cancel' + (this.message ? ': ' + this.message : '');
};

Cancel.prototype.__CANCEL__ = true;

module.exports = Cancel;

建立Cancel的構造函數,代表取消某個請求,提供一句取消信息和標記app

axios/lib/cancel/CancelToken.js

var Cancel = require('./Cancel');

/**
 * A `CancelToken` is an object that can be used to request cancellation of an operation.
 *
 * @class
 * @param {Function} executor The executor function.
 */
function CancelToken(executor) {
  if (typeof executor !== 'function') {
    throw new TypeError('executor must be a function.');
  }

  var resolvePromise;
  this.promise = new Promise(function promiseExecutor(resolve) {
    resolvePromise = resolve;
  });

  var token = this;
  executor(function cancel(message) {
    if (token.reason) {
      // Cancellation has already been requested
      return;
    }

    token.reason = new Cancel(message);
    resolvePromise(token.reason);
  });
}

/**
 * Throws a `Cancel` if cancellation has been requested.
 */
CancelToken.prototype.throwIfRequested = function throwIfRequested() {
  if (this.reason) {
    throw this.reason;
  }
};

/**
 * Returns an object that contains a new `CancelToken` and a function that, when called,
 * cancels the `CancelToken`.
 */
CancelToken.source = function source() {
  var cancel;
  var token = new CancelToken(function executor(c) {
    cancel = c;
  });
  return {
    token: token,
    cancel: cancel
  };
};

module.exports = CancelToken;

建立CancelToken對象用於取消請求的操做:函數

  • 定義新的Promise並將改變狀態的觸發時機傳遞到外部
  • 執行傳入的回調函數參數

    • 若是已經含有reason屬性表明已經發出取消請求的操做,則中斷操做
    • 不然建立新的Cancel對象而且改變Promise狀態返回該對象

原型上綁定throwIfRequested方法,若是實例已經取消能夠直接拋出取消信息

構造函數上綁定source方法,調用以後能夠返回新的CancelToken實例和cancel方法

axios/lib/cancel/isCancel.js

module.exports = function isCancel(value) {
  return !!(value && value.__CANCEL__);
};

經過是否擁有__CANCEL__屬性而得知某次請求是否已經取消了.

示例

const CancelToken = axios.CancelToken;
const source = CancelToken.source();

axios.get('/user/12345', {
  cancelToken: source.token
}).catch(function(thrown) {
  if (axios.isCancel(thrown)) {
    console.log('Request canceled', thrown.message);
  } else {
     // 處理錯誤
  }
});

axios.post('/user/12345', {
  name: 'new name'
}, {
  cancelToken: source.token
})

// 取消請求(message 參數是可選的)
source.cancel('Operation canceled by the user.');

或者

const CancelToken = axios.CancelToken;
let cancel;

axios.get('/user/12345', {
  cancelToken: new CancelToken(function executor(c) {
    // executor 函數接收一個 cancel 函數做爲參數
    cancel = c;
  })
});

// cancel the request
cancel();
相關文章
相關標籤/搜索