# Axios 用法及配置

# 1. 常用配置项

# 1. 发起 GET 请求

// Make a request for a user with a given ID
axios.get('/user?ID=12345')
  .then(response=> {
    console.log(response);
  })
  .catch(error=> {
    console.log(error);
  });
// Optionally the request above could also be done as
axios.get('/user', {
    params: {
      ID: 12345
    }
  })
  .then(response => {
    console.log(response);
  })
  .catch(error=> {
    console.log(error);
  });

# 2. 发起 POST 请求

axios.post('/user', {
    firstName: 'Fred',
    lastName: 'Flintstone'
  })
  .then(response=> {
    console.log(response);
  })
  .catch(error=> {
    console.log(error);
  });

# 3. 同时发起多个请求

function getUserAccount() {
  return axios.get('/user/12345');
}
function getUserPermissions() {
  return axios.get('/user/12345/permissions');
}
axios.all([getUserAccount(), getUserPermissions()])
  .then(axios.spread((acct, perms)=> {
    // Both requests are now complete
  }));

# 2.axios api (可以通过导入相关配置发起请求)

# 1.axios(config)

// 发起一个 POST 请求
axios({
  method: 'post',
  url: '/user/12345',
  data: {
    firstName: 'Fred',
    lastName: 'Flintstone'
  }
});
// 获取远程图片
axios({
    method: 'get',
    url: 'http://bit.ly/2mTM3ny',
    responseType: 'stream'
}).then( response=>{
    response.data.pipe(fs.createWriteStream('ada_lovelace.jpg'))
})

# 2.axios(url[,config])

// 发起一个 GET 请求 (GET 是默认的请求方法)
axios('/user/111');

请求方法别名: axios 为所有支持的请求方法均提供了别名

使用以下别名方法时,url, method 和 data 等属性不用在 config 重复声明

  • axios.request(config)
  • axios.get(url[,config])
  • axios.delete(url[,config])
  • axios.head(url[,config])
  • axios.options(url[,config])
  • axios.post(url,data[,config])
  • axios.put(url[,data[,config]])
  • axios.patch(url[,data[,config]])

同时发生的请求

  • axios.all(iterable)
  • axios.sperad(callback)

# 3. 创建 axios 实例

# 1. 创建拥有通用配置的 axios 实例

axios.create([config])

var instance = axios.create({
    baseURL: 'http://some-domain.com/api',
    timeout: 1000,
    headers: {'X-Custom-Header': 'foobar'}
})

实例的方法

  • axios#request(config)
  • axios#get(url[, config])
  • axios#delete(url[, config])
  • axios#head(url[, config])
  • axios#options(url[, config])
  • axios#post(url[, data[, config]])
  • axios#put(url[, data[, config]])
  • axios#patch(url[, data[, config]])

请求配置

以下是所有可用的请求配置项,只有 url 是必填,如果没有指定请求方式,默认请求方法是 GET

{
    // `url` 是请求接口地址
    url: '/Info',
    
    // `method` 是请求方法 默认值: GET
    method: 'get',
    
   // 如果 URL 不是绝对路径,那么将 baseURL 和 url 拼接作为请求的接口地址
   // 用来区分不同环境
   baseURL: 'http://some-domain.com/api/',
   
   // 用于请求之前对请求数据进行操作
   // 请求方法为 `PUT`, `POST` 和 `PATCH` 时可用
   // 最后一个函数需 return 出相应数据
   // 可以修改 headers
   transformRequest: [(data, headers)=>{
       // 对 data 进行一些操作
       
       return data
   }],
	// `headers` are custom headers to be sent
	headers: {'X-Requested-With': 'XMLHttpRequest'},
	// URL 参数
	// 必须是一个纯对象或者 URL 参数对象
	params: {
		ID: 10010
	},
	// 负责序列化 `params` 的可选函数
	paramsSerializer: params=>{
		return Qs.stringify(params,{arrayFormat: 'brackets'})
	}
	// 请求体数据
	// 请求方法为 `PUT`, `POST` 和 `PATCH` 时可用
	// 当没有设置 `transformRequest` 时,必须是一下几种格式
	// - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
  	// - Browser only: FormData, File, Blob
  	// - Node only: Stream, Buffer
  	data: {
  		firstName: 'Fred'
  	},
  	// 请求超时时间 (毫秒)
  	timeout: 1000,
  	// 是否携带 cookie 信息 默认值:false
  	withCredentials: false, 
  	// 统一处理 request 让测试更加容易
  	// 返回一个 promise 并提供一个可用的 response
  	adapter: config =>{
  		/*...*/
  	},
  	// `auth` indicates that HTTP Basic auth should be used, and supplies credentials.
  	// This will set an `Authorization` header, overwriting any existing
  	// `Authorization` custom headers you have set using `headers`.
  	auth: {
  		username: 'janedoe',
  		password: 's00pers3cret'
 	},
 	// 响应格式 默认值: json
 	// 可选项 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream'
 	responseType: 'json',
 	// `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
  	// 处理上传进度事件
  	onUploadProgress: progressEvent=> {
  		// Do whatever you want with the native progress event
	},
	// 处理下载进度事件
	onDownloadProgress: function (progressEvent) {
	    // Do whatever you want with the native progress event
	},
	// 设置 http 响应内容的最大长度
	maxContentLength: 2000,
	// 定义可获得的 http 响应状态码
	//return true, 设置为 null 或者 undefined, promise 将 resolved, 否则将 rejected
	validateStatus: status =>{
		return status >= 200 && status < 300 // default
	},
	// 最大重定向次数
	maxRedirects: 5, // 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 and port of the proxy server
  	// Use `false` to disable proxies, ignoring environment variables.
 	// `auth` indicates that HTTP Basic auth should be used to connect to the proxy, and
 	// supplies credentials.
 	// This will set an `Proxy-Authorization` header, overwriting any existing
  	// `Proxy-Authorization` custom headers you have set using `headers`.
  	// 代理
  	proxy: {
    	host: '127.0.0.1',
    	port: 9000,
    	auth: {
      		username: 'mikeymike',
      	password: 'rapunz3l'
    	}
 	},
 	// `cancelToken` specifies a cancel token that can be used to cancel the request
  	// (see Cancellation section below for details)
  	// 用于取消请求
  	cancelToken: new CancelToken(cancel=>{...})
}

# 2. 取消重复的请求

  • 利用 CancelToken 工厂函数创建 cancel token
const CancelToken = axios.CancelToken
const source = CancelToken.source()
//get 方法使用案例
axios.get('user/111',{
    cancelToken: source.token
}).catch(thrown => {
    if(axios.isCancel(thrown)){
        console.log('Request canceled', thrown.message)
    } else {
        // TODO: handle error
    }
})
//post 使用案例
axios.post('/user/111',{
    name: 'name '
},{
    cancelToken: source.token
})
// 执行取消请求操作
source.cancel('请求已取消')
  • 传递 executor 函数到 CancelToken 的构造函数来创建 cancel token
  • 不同请求可以定义同一个取消请求的方法名,来批量取消多个请求
const CancelToken = axios.CancelToken
let cancel
axios.get('/user/111',{
    cancelToken: new CancelToken(function executor(c) {
        cancel = c
    })
})
// 执行取消请求操作
cancel()

# 3. 响应数据组成 response

  • response 由一下部分信息组成
{
    // 服务端返回的状态码
	status: 200,    
    
    // 服务端返回数据
    data: {},
    
    // 服务端返回的状态信息
    statusText: 'OK',
    
    // 响应头:响应头名称都是小写
    headers: {},
        
    //axios 请求配置
    config: {},
        
    // 请求
    request: {}
}
  • 用 then 接收响应信息
axios.get('/user/111')
	.then(response=>{
    	console.log(response.status)
    	console.log(response.data)
   		console.log(response.statusText)
    	console.log(response.headers)
    	console.log(response.config)
})

# 4. 默认配置

全局修改 axios 默认配置

axios.default.baseURL = 'https://api.example.com';
axios.default.headers.common['Authorization'] = AUTH_TOKEN;
axios.default.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';

实例默认配置

// 创建实例时修改配置
var instance = axios.create({
    baseURL: 'https:/api.example.com'
})
// 实例创建之后修改配置
instance.defaults.headers.common['Authorization'] = AUTH_TOKEN;

配置优先级

配置项通过一定的规则合并,request config > instance.defaults > 系统默认,优先低的被覆盖

// 创建一个实例,超时时间为系统默认的 0
var instance = axios.create();
// 通过 instance.defaults 重新设置超时时间为 2.25s, 优先级比系统默认高
instance.defaults.timeout = 2500;
// 通过 request config 重新设置超时时间为 5s, 因为优先级比 instance.defaults 和系统默认都高
instance.get('/longRequest',{
    timeout: 5000
})

# 5. 拦截器

  1. 在 then 和 catch 之前拦截请求和响应
// 添加一个请求拦截器
axios.interceptors.request.use(config =>{
    // 在发送请求前执行一些操作
    return config
},error=>{
    // 处理请求错误
    return Promise.reject(error)
})
// 添加一个响应拦截器
axios.interceptors.response.use(response=> {
    // 处理响应数据
    return response
}, error=> {
    // 处理响应错误
    return Promise.reject(error)
})
  1. 移除拦截器
// 创建一个请求拦截器
var myInterceptor = axios.interceptors.request.use(()=>{/*...*/})
// 移除拦截器
axios.interceptors.request.eject(myInterceptor)
  1. 为 axios 实例添加一个拦截器
var instance = axios.create()
instance.interceptors.request.use(()=>{/*...*/})
  1. 错误处理
axios.get('/user/111')
.catch(error=> {
    if (error.response) {
        // 发送请求后,服务端返回响应码不是 2xx
        console.log(error.response.data)
        console.log(error.response.status)
        console.log(error.response.headers)
    } else if (error.request) {
        // 发送请求但是没有响应返回
        console.log(error.request)
    } else {
        // 未知错误
        console.log('Error',error.message)
    }
    console.log(error.config)
})

# 4.axios 无感知刷新 token

import axios from "axios"; // 引入 axios
import qs from "qs";// 序列化 post 接口请求参数
import LocalStorage from "./localStorage.js"; //H5 本地存储封装方法
let SECURITY_URL = "http://192.168.1.17:5505/security-amass/";// 刷新 token 接口的 base 路径
axios.defaults.headers.post["Content-Type"] ="application/x-www-form-urlencoded;charset=UTF-8";
axios.defaults.headers.post["Access-Control-Allow-Origin"] = "*";
axios.defaults.withCredentials = false; // 携带 cookie
// 创建一个 axios 实例
const instance = axios.create({
  timeout: 300000,
});
// 是否正在请求刷新 token 接口的标记
let isRefreshing = false;
// 请求队列
let requests = [];
// 刷新 token 方法
function refreshToken(params) {
  return instance.get(SECURITY_URL + "oauth/token", params);
}
// 请求结果拦截器
instance.interceptors.response.use(
  (response) => {
    // 接下来会在这里进行 token 过期的逻辑处理
    const config = response.config;
    let code = response.data.code;
    // 这里的 code 值是跟后端约定好的, 40009 代表 token 已经过期
    if (code == "40009") {
      if (!isRefreshing) {
        isRefreshing = true;
        let refresh_token = LocalStorage.get("refresh_token");
        // 这里是我的项目中刷新 token 要传的参数,具体都传那些参数需要与后端开发确认
        let loginData = {
          grant_type: "refresh_token",
          client_id: "impawning",
          client_secret: "impawning",
          refresh_token,
        };
        refreshToken({ params: loginData })
          .then((res) => {
            let access_token = res.data.access_token;
            let refresh_token = res.data.refresh_token;
            LocalStorage.set("access_token", res.data.access_token);
            LocalStorage.set("refresh_token", res.data.refresh_token);
            config.headers["access_token"] = access_token;
            config.headers["refresh_token"] = refresh_token;
            requests.forEach((cb) => cb(access_token, refresh_token));
            requests = [];
            return instance(config);
          })
          .catch((err) => {
            window.location.href = "/";
          })
          .finally(() => {
            isRefreshing = false;
          });
      } else {
        // 正在刷新 token,返回一个未执行 resolve 的 promise
        return new Promise((resolve) => {
          // 将 resolve 放进队列,用一个函数形式来保存,等 token 刷新后直接执行
          requests.push((access_token, refresh_token) => {
            config.headers["access_token"] = access_token;
            config.headers["refresh_token"] = refresh_token;
            resolve(instance(config));
          });
        });
      }
    } else if (code == "40005" || code == "40003") {// 这里代表 token 失效和 token 错误
      window.location.href = "/";
    } else {
      return response;
    }
  },
  (error) => {
    return Promise.reject(error);
  }
);
const api = {
  get(url, data) {
    instance.defaults.headers.common["access_token"] = LocalStorage.get(
      "access_token"
    );
    instance.defaults.headers.common["refresh_token"] = LocalStorage.get(
      "refresh_token"
    );
    return instance.get(url, { params: data });
  },
  post(url, data) {
    instance.defaults.headers.common["access_token"] = LocalStorage.get(
      "access_token"
    );
    instance.defaults.headers.common["refresh_token"] = LocalStorage.get(
      "refresh_token"
    );
    //post 接口封装
    return instance.post(url, qs.stringify(data));
  },
};
export { api };