axios学习笔记

150 阅读6分钟

1、axios简介

axios:基于promise可以用于浏览器和node.js的网络请求库
特性
  • 在服务器端,基于node的http模块
  • 在浏览器端,使用XMLHttpRequests实现;
  • 使用Promise API
  • 拦截请求和响应
  • 对请求和响应数据进行转换
  • 可以取消请求
  • 自动转换成json数据格式
  • 客户端支持防护XSRF安全攻击
安装
//npm
npm install axios
//yarn
yarn add axios
//CDN
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>

2、axios使用

简单使用说明
  • Get请求
const axios = require('axios');

// 向给定ID的用户发起请求
axios.get('/user?ID=12345')
  .then(function (response) {
    // 处理成功情况
    console.log(response);
  })
  .catch(function (error) {
    // 处理错误情况
    console.log(error);
  })
 

// request中的querystring被param代替
axios.get('/user', {
    params: {
      ID: 12345
    }
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  })
 

// 支持async/await + try-catch固定写法
async function getUser() {
  try {
    const response = await axios.get('/user?ID=12345');
    console.log(response);
  } catch (error) {
    console.error(error);
  }
}
  • Post请求
axios.post('/user', {
    firstName: 'Fred',
    lastName: 'Flintstone'
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });
  • 处理多个请求
function getUserAccount() {
  return axios.get('/user/12345');
}

function getUserPermissions() {
  return axios.get('/user/12345/permissions');
}

//使用promise.all方法
Promise.all([getUserAccount(), getUserPermissions()])
  .then(function (results) {
    const acct = results[0];
    const perm = results[1];
  });
//使用async、await
aysnc function getAcountAndPerm(){
	try{
		const acct = await getUserAccount();
		const perm = await getUserPermissions();
	}catch(err){
		console.log(err)
	}
}
axios API文档
//当做函数使用,相当于axios.request
axios(config)
axios(url[,config])
//当做对象使用,使用原型方法
//将url从config中提出来,config是可选的
axios.request(config)
axios.get(url[,config])
axios.delete(url[,config])
axios.head(url[,config])
axios.option(url[,config])
//可以将url和data从config中提出来,config是可选的
axios.post(url[,data[,config]])
axios.put(url[,data[,config]])
axios.patch(url[,data[,config]])

//创建axios实例对象,instance和axios默认api一致,可以有一些个性化的参数配置,同时少一些axios属性。
const instance = axios.create([config])

axios 请求配置
//config对象内容
{
  // `url`请求路径
  url: '/user',

  // `method` 请求方法,默认是get
  method: 'get', 

  // `baseURL` url前缀
  baseURL: 'https://some-domain.com/api/',

  // `transformRequest` 请求数据转换
  transformRequest: [function (data, headers) {
    return data;
  }],

  // `transformResponse` 响应数据转换
  transformResponse: [function (data) {
      return data;
  }],

  // `headers` 请求头
  headers: {'X-Requested-With': 'XMLHttpRequest'},

  // `params` 请求参数
  params: {
    ID: 12345
  },

  // `paramsSerializer` 参数序列化方式
  paramsSerializer: function (params) {
    return Qs.stringify(params, {arrayFormat: 'brackets'})
  },

  // `data` 请求数据
  data: {
    firstName: 'Fred'
  },
  
  // `timeout` 请求超时时间 毫秒
  timeout: 1000, 

  // `withCredentials` 是否允许跨域
  withCredentials: false, 

  // `adapter` 请求适配器,自定义实践用于测试
  adapter: function (config) {
    /* ... */
  },

  // `auth` 请求认证信息
  auth: {
    username: 'janedoe',
    password: 's00pers3cret'
  },

  // `responseType` 返回数据类型
  responseType: 'json', 

  // `responseEncoding` 返回数据编码
  responseEncoding: 'utf8',

  // `xsrfCookieName`  xsrf token
  xsrfCookieName: 'XSRF-TOKEN', 

  // `xsrfHeaderName` xsrf token value
  xsrfHeaderName: 'X-XSRF-TOKEN', 

  // `onUploadProgress` 上传事件处理钩子
  onUploadProgress: function (progressEvent) {

  },

  // `onDownloadProgress` 下载事件处理钩子
  onDownloadProgress: function (progressEvent) {
  
  },

  // `maxContentLength` node最大处理长度
  maxContentLength: 2000,

  // `maxBodyLength` (Node only option) 
  maxBodyLength: 2000,

  // `validateStatus` 成功状态判定
  validateStatus: function (status) {
    return status >= 200 && status < 300; 
  },

  // `maxRedirects` 最大转发次数
  maxRedirects: 21, 

  // `beforeRedirect` 
  beforeRedirect: (options, { headers }) => {
    if (options.hostname === "example.com") {
      options.auth = "user:password";
    }
  };

  // `socketPath` defines a UNIX Socket to be used in node.js.
  socketPath: null, // default

  // `httpAgent` and `httpsAgent` define a custom agent to be used when performing http
  // and https requests, respectively, in node.js. This allows options to be added like
  // `keepAlive` that are not enabled by default.
  httpAgent: new http.Agent({ keepAlive: true }),
  httpsAgent: new https.Agent({ keepAlive: true }),

  // `proxy` defines the hostname, port, and protocol of the proxy server.
  proxy: {
    protocol: 'https',
    host: '127.0.0.1',
    port: 9000,
    auth: {
      username: 'mikeymike',
      password: 'rapunz3l'
    }
  },

  // `cancelToken` 定义取消请求对象
  cancelToken: new CancelToken(function (cancel) {
  }),
  // 另一种取消请求方式
  signal: new AbortController().signal,
  // - Node only (XHR cannot turn off decompression)
  decompress: true // default

  // `insecureHTTPParser` boolean.
  insecureHTTPParser: undefined 

  env: {
     FormData: window?.FormData || global?.FormData
  }
}
axios响应对象
{
  // `data` 服务端返回数据
  data: {},

  // `status` HTTP响应码
  status: 200,

  // `statusText` HTTP响应文本
  statusText: 'OK',

  // `headers` http响应头
  headers: {},

  // `config` 请求的request config对象
  config: {},

  // `request` 请求体
  request: {}
}
拦截器

axios-interceptor.png

// 增加一个请求拦截器 req1
axios.interceptors.request.use(function req1(config) {
    // Do something before request is sent
    return config;
  }, function (error) {
    // Do something with request error
    return Promise.reject(error);
  });
// 增加一个请求拦截器 req2
axios.interceptors.request.use(function req2(config) {
    // Do something before request is sent
    return config;
  }, function (error) {
    // Do something with request error
    return Promise.reject(error);
  });
// 增加一个响应拦截器 res1
axios.interceptors.response.use(function res1(response) {
    // Do something with response data
    return response;
  }, function (error) {
    // Do something with response error
    return Promise.reject(error);
  });
// 增加一个响应拦截器res2
axios.interceptors.response.use(function res1(response) {
    // Do something with response data
    return response;
  }, function (error) {
    // Do something with response error
    return Promise.reject(error);
  });
  
  • request 拦截器传递修改的是config请求配置对象
  • response拦截器传递修改的是response返回体对象
  • 按照执行器的实现逻辑,请求拦截器会在对头插入,响应拦截器会在队尾插入。因此上述代码执行顺序是:req2-->req1-->dispatch-->res1-->res2
取消请求
  • CancelToken(不推荐使用了)
const CancelToken = axios.CancelToken;
let cancel;

//请求时config中配置cancelToken
axios.get('/user/12345', {
  cancelToken: new CancelToken(function executor(c) {
    cancel = c;
  })
});

// 取消请求,用户需要时
cancel();
  • AbortController
const controller = new AbortController();

axios.get('/foo/bar', {
   signal: controller.signal
}).then(function(response) {
   //...
});
// 取消请求
controller.abort()

3、axios核心源码

文件目录
|-- lib
    |-- axios.js
    |-- utils.js
    |-- adapters
    |   |-- http.js
    |   |-- README.md
    |   |-- xhr.js
    |-- cancel
    |   |-- Cancel.js
    |   |-- CancelToken.js
    |   |-- isCancel.js
    |-- core
    |   |-- Axios.js
    |   |-- buildFullPath.js
    |   |-- createError.js
    |   |-- dispatchRequest.js
    |   |-- enhanceError.js
    |   |-- InterceptorManager.js
    |   |-- mergeConfig.js
    |   |-- README.md
    |   |-- settle.js
    |   |-- transformData.js
    |-- defaults
    |   |-- index.js
    |   |-- transitional.js
    |-- env
    |   |-- data.js
    |   |-- README.md
    |-- helpers
        |-- bind.js
        |-- buildURL.js
        |-- combineURLs.js
        |-- cookies.js
        |-- deprecatedMethod.js
        |-- isAbsoluteURL.js
        |-- isAxiosError.js
        |-- isURLSameOrigin.js
        |-- normalizeHeaderName.js
        |-- parseHeaders.js
        |-- README.md
        |-- spread.js
        |-- toFormData.js
        |-- validator.js
core/Axios.js
//构造函数,并增加defauts和interceptors两个实例对象属性
function Axios(instanceConfig) {
  this.defaults = instanceConfig;
  //拦截器熟悉包含request,和response两个对象,值为InterceptorManager对象
  this.interceptors = {
    request: new InterceptorManager(),
    response: new InterceptorManager()
  };
}
  • request函数
//Axios原型方法上,最核心的request函数
Axios.prototype.request = function request(configOrUrl, config) {
  if (typeof configOrUrl === 'string') {
    config = config || {};
    config.url = configOrUrl;
  } else {
    config = configOrUrl || {};
  }
  config = mergeConfig(this.defaults, config);
  // Set config.method
  if (config.method) {
    config.method = config.method.toLowerCase();
  } else if (this.defaults.method) {
    config.method = this.defaults.method.toLowerCase();
  } else {
    config.method = 'get';
  }
  var transitional = config.transitional;
  if (transitional !== undefined) {
    validator.assertOptions(transitional, {
      silentJSONParsing: validators.transitional(validators.boolean),
      forcedJSONParsing: validators.transitional(validators.boolean),
      clarifyTimeoutError: validators.transitional(validators.boolean)
    }, false);
  }
  // 添加实例请求拦截器,unshift到队头
  var requestInterceptorChain = [];
  var synchronousRequestInterceptors = true;
  this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
    if (typeof interceptor.runWhen === 'function' && interceptor.runWhen(config) === false) {
      return;
    }
    synchronousRequestInterceptors = synchronousRequestInterceptors && interceptor.synchronous;

    requestInterceptorChain.unshift(interceptor.fulfilled, interceptor.rejected);
  });
  //添加响应拦截器,push到队尾
  var responseInterceptorChain = [];
  this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
    responseInterceptorChain.push(interceptor.fulfilled, interceptor.rejected);
  });

  var promise;

  if (!synchronousRequestInterceptors) {
    //实际请求处理,reject为undefined起到一个占位作用
    var chain = [dispatchRequest, undefined];

    Array.prototype.unshift.apply(chain, requestInterceptorChain);
    chain = chain.concat(responseInterceptorChain);

    promise = Promise.resolve(config);
    while (chain.length) {
      promise = promise.then(chain.shift(), chain.shift());
    }

    return promise;
  }


  var newConfig = config;
   //请求拦截器执行,onFullfilled有用户config传入,并修改config
  while (requestInterceptorChain.length) {
    var onFulfilled = requestInterceptorChain.shift();
    var onRejected = requestInterceptorChain.shift();
    try {
      newConfig = onFulfilled(newConfig);
    } catch (error) {
      onRejected(error);
      break;
    }
  }
    //执行dispatchRequest-->adapter方法完成实际请求,返回一个promise
  try {
    promise = dispatchRequest(newConfig);
  } catch (error) {
    return Promise.reject(error);
  }
	//通过then方法执行响应拦截器,处理response对象
  while (responseInterceptorChain.length) {
    promise = promise.then(responseInterceptorChain.shift(), responseInterceptorChain.shift());
  }

  return promise;
};
// 定义delete、get、head、options方法,使用request实现
utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
   //在Axios原型对象上加上这些熟悉方法
  Axios.prototype[method] = function(url, config) {
    return this.request(mergeConfig(config || {}, {
      method: method,
      url: url,
      data: (config || {}).data
    }));
  };
});
// 定义post、put、patch方法,使用request实现,比上面多了data属性
utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
  /*eslint func-names:0*/
  Axios.prototype[method] = function(url, data, config) {
    return this.request(mergeConfig(config || {}, {
      method: method,
      url: url,
      data: data
    }));
  };
});
core/InterceptorManager

拦截器相关对象

//函数对象,包含一个拦截器数组
function InterceptorManager() {
  this.handlers = [];
}
//函数对象原型上增加use方法,实际是往handlers对象中添加成功和失败回调函数
InterceptorManager.prototype.use = function use(fulfilled, rejected, options) {
  this.handlers.push({
    fulfilled: fulfilled,
    rejected: rejected,
    synchronous: options ? options.synchronous : false,
    runWhen: options ? options.runWhen : null
  });
  return this.handlers.length - 1;
};
core/dispatchRequest.js

dispatchRequest将请求转发到相应的adapter上,完成请求的发送和处理。

module.exports = function dispatchRequest(config) {
 //忽略了一些异常处理和headers相关代码

  //查看request config中是否配置了适配器,如果没有从defaults中获取
  //后面分析
  var adapter = config.adapter || defaults.adapter;

  /***
  //以下代码在defaults/index.js中可以看出,默认的adapter是根据如下规则进行判断
  //如果有XMLHttpRequest,则为浏览器端,使用xhrAdapter
  //如果有process对象,则为node端,使用httpAdapter
  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;
    }
  **/
  return adapter(config).then(function onAdapterResolution(response) {
    throwIfCancellationRequested(config);

    // Transform response data
    response.data = transformData.call(
      config,
      response.data,
      response.headers,
      config.transformResponse
    );

    return response;
  }, function onAdapterRejection(reason) {
    if (!isCancel(reason)) {
      throwIfCancellationRequested(config);

      // Transform response data
      if (reason && reason.response) {
        reason.response.data = transformData.call(
          config,
          reason.response.data,
          reason.response.headers,
          config.transformResponse
        );
      }
    }
    return Promise.reject(reason);
  });
};
adapters
名称作用
adapters/xhr.js基于ajax封装浏览器端实际请求的实现
adapters/http.js基于http/https实现node端网络请求