Axios二次封装

8,136 阅读3分钟

axios是一个基于Promise的http库,可以用在浏览器和node.js中。同时也是对原生浏览器请求XMLHttpRequest的封装,支持Promise的APi请求,避免了回掉地狱问题,可以对请求进行拦截,在发出请求前对请求参数进行修改,接受服务器响应时,也可以根据返回的code进行统一的处理,且客户端支持防御XSRF。可以开箱即用,但是在实际项目时,需要对axios进行二次封装

实例 Or defaults

对 axios 进行二次封装由两种方式,一种是创建一个axios实例,另外一种是直接修改axios的defaults

import axios from 'axios';
var instance = axios.create({
  baseURL: 'https://some-domain.com/api/',
  timeout: 1000,
  headers: {'X-Custom-Header': 'foobar'}
});
import axios from 'axios';
axios.defaults.baseURL = SERVICE;

当然在使用第一种创建一个实例时,也可以设置这个实例的defaults,就像这样

import axios from 'axios';
var instance = axios.create({
  baseURL: 'https://some-domain.com/api/',
  timeout: 1000,
  headers: {'X-Custom-Header': 'foobar'}
});
instance.defaults.baseURL = SERVICE;

设置baseURL

在前后端分离项目,往往需要定义一个全局变量来声明后台的接口地址,一般我会选择通过 webpack 的 definePlugin 插件来给页面设置全局变量,并且根据不同的环境传递不同的值。比如说后台的接口部署在了 http://localhost:3000 那么首先使用 webpack 定义一个叫 SERVICE 的全局变量

new webpack.DefinePlugin({
    SERVICE: "'http://localhost:3000'"
})

然后就可以在页面中使用这个全局变量,当然如果是在ts项目下的话,直接使用SERVICE会抱一个未定义的错,那么只需要在使用SERVICE的文件中申明一下即可,未使用ts 的可以跳过这句声明。

import axios from 'axios';
// ts 下使用 需要先声明
declare const SERVICE: string;
axios.defaults.baseURL = SERVICE;

设置Content-type

在 post 请求和 put 请求中,需要在请求头里设置一下content-type 为 application/x-www-form-urlencoded

axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
axios.defaults.headers.put['Content-Type'] = 'application/x-www-form-urlencoded';

设置withCredentials

跨域请求时,默认是不会携带cookie的,这就导致了在前后端分离项目中,做一个登录的页面,登录完之后跳转主页面,但是主页面中请求的接口未检测到用户已登录那么就会跳回登录页面,大概时这样的场景。那么缘由就是因为axios在发起请求时没有携带cookie的,通过设置withCredentials为true 来解决

axios.defaults.withCredentials = true;

请求拦截 request处理

可以通过对请求的拦截,修改参数,对参数进行序列化处理,防止XSRF攻击。序列化使用 qs来实现,qs的优点是可以对深层次的json array 等复杂类型进行序列化。

axios.interceptors.request.use((config: any): any => {
    // 给请求添加请求时间
    if (config.url.indexOf('?') !== -1) {
        config.url += `&t=${new Date().getTime()}`;
    } else {
        config.url += `?t=${new Date().getTime()}`;
    }
    // `transformRequest` 允许在向服务器发送前,修改请求数据
    // 只能用在 'PUT', 'POST''PATCH' 这几个请求方法
    // 后面数组中的函数必须返回一个字符串,或 ArrayBuffer,或 Stream
    config.transformRequest = [(data: any, headers: any) => {
        return qs.stringify(data, {
            allowDots: true
        })
    }];
    // `paramsSerializer` 是一个负责 `params` 序列化的函数
    config.paramsSerializer = (params: any) => {
        return qs.stringify(params, {
            arrayFormat: 'repeat'
        })
    };
    return config;
}, (error: any) => {
    return Promise.reject(error);
});

对response进行处理

有这么一个场景,当接口返回的code为2时,表明用户未登录,这时需要前端对请求的response返回的code进行判断,那么一个请求一个请求的判断肯定是会很麻烦的,哪个请求忘了加判断就完蛋了,所以axios提供了对响应进行拦截的操作。

axios.interceptors.response.use((response: any): any => {
    const { data } = response;
    if(data.code === 2){
        window.location.href = `/login?from=${window.location.pathname}`;
    }
    return response;
}, (error: any) => {
    Promise.reject(error);
});

axios常规操作

GET请求

get请求,对于需要参数的get请求,请一定要将参数放在 params 里,不然你会吃亏的。使用场景如下,获取一个邮件的id,mailId是一个非常长的一串各种字符组成的,恰巧这里面包含了一些 . 或者 \ 或者 : 啥的,具体的我也不知道,最后导致的原因是这个邮箱的mailID穿不到后台,因为这个参数直接放在路由后面是有问题的,所以请直接讲参数放在params里,因为上面已经对params里的参数进行了处理。

axios.get('/api/info',{
    params: {
        id: 1
    }
}).then(res => {

}).catch(err => {

})

POST请求

axios.post('/api/info',{
    username: 'haha',
    password: '123456'
}).then(res => {

}).catch(err => {

})

delete 请求类似于get请求 put请求类似于post请求

链式调用

axios.get('/api/info',{
    params: {
        id: 1
    }
}).then(res => {
    return axios.post('/api/info',{
        username: 'haha',
        password: '123456'
    });
}).then(res => {

}).catch(err => {

})

axios.all

Promise.all 类似,用于多个请求并罚处理,等待请求全部完成时执行回调,并且回调参数为一个数组,数组里的顺序与请求的顺序是一致的,也就是说他是按顺序将返回值存进去的

axios.all([
    axios.get('/api/info',{
        params: {
            id: 1
        }
    }),
    axios.post('/api/info',{
        username: 'haha',
        password: '123456'
    });
]).then(resArray => {
    // resArray[0] 为axios.get('/api/info') 的res
    // resArray[1] 为axios.post('/api/info') 的res
}).catch(err => {

})

完整代码-模块化开发

封装完了之后将axios导出,其他的页面直接引用该axios就行。完整代码如下

// request.js
import axios from 'axios';
import * as qs from 'qs';

declare const SERVICE: string;

axios.defaults.baseURL = SERVICE;
axios.defaults.withCredentials = true;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
axios.defaults.headers.put['Content-Type'] = 'application/x-www-form-urlencoded';

// qs 序列化 防止XSRF攻击 可以对深层次的json array进行序列化
axios.interceptors.request.use((config: any): any => {
    if (config.url.indexOf('?') !== -1) {
        config.url += `&t=${new Date().getTime()}`;
    } else {
        config.url += `?t=${new Date().getTime()}`;
    }
    // `transformRequest` 允许在向服务器发送前,修改请求数据
    // 只能用在 'PUT', 'POST''PATCH' 这几个请求方法
    // 后面数组中的函数必须返回一个字符串,或 ArrayBuffer,或 Stream
    config.transformRequest = [(data: any, headers: any) => {
        return qs.stringify(data, {
            allowDots: true
        })
    }];
    // `paramsSerializer` 是一个负责 `params` 序列化的函数
    config.paramsSerializer = (params: any) => {
        return qs.stringify(params, {
            arrayFormat: 'repeat'
        })
    };
    return config;
}, (error: any) => {
    return Promise.reject(error);
});

axios.interceptors.response.use((response: any): any => {
    const { data } = response;
    if(data.code === 2){
        window.location.href = `/login?from=${window.location.pathname}`;
    }
    return response;
}, (error: any) => {
    Promise.reject(error);
});

export default axios;
// action.js
import axios from './request';
axios.get('')
    .then(res = > {
        // 业务代码
    })
    .catch(err => {
        // 业务代码
    })