Axios二次封装

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

实例 Or defaults

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

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,就像这样webpack

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 的全局变量ios

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

而后就能够在页面中使用这个全局变量,固然若是是在ts项目下的话,直接使用SERVICE会抱一个未定义的错,那么只须要在使用SERVICE的文件中申明一下便可,未使用ts 的能够跳过这句声明。web

import axios from 'axios';
// ts 下使用 须要先声明
declare const SERVICE: string;
axios.defaults.baseURL = SERVICE;
复制代码

设置Content-type

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

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

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提供了对响应进行拦截的操做。api

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 => {
        // 业务代码
    })
复制代码