axios封装 axios封装以及取消发送请求_axios统一封装

目录

本文介绍axios的特点,简单的操作,可配置属性,统一的封装(适合大型项目)以及一些高级操作,比如取消请求,请求重试,上传文件,xsrf攻击防御。

前言

作为一个前端码农,应该明白 Ajax(Asynchronous JavaScript and XML,即异步网络请求)技术的出现对现在前端技术发展是巨大的促进的。但是对于不同的浏览器对ajax的支持并不同,需要统一兼容api,而且 ajax 调用接口很容易形成回调地狱。

因此 axios 横空出世,接下来我将介绍 axios 的特点和用户,并有相应的例子。

「Axios特点」

  • 从浏览器中创建 XMLHttpRequests
  • 从 node.js 创建 http 请求
  • 支持 Promise API
  • 拦截请求和响应
  • 转换请求数据和响应数据
  • 取消请求
  • 自动转换 JSON 数据
  • 客户端支持防御 XSRF

「Axios 基本操作」

npm install axios
  • GET
function get(url) {
  return axios.get(url, {
    method: "get",
    baseURL: "http://localhost:3001",
    params: {},
    timeout: 1000,
    timeoutErrorMessage: "请求超时",
  });
}

正常返回

axios封装 axios封装以及取消发送请求_axios封装_02

请求超时

axios封装 axios封装以及取消发送请求_axios统一封装_03

  • POST
function post(url, data) {
  return axios.post(url, data, {
    baseURL: "http://localhost:3001",
    timeout: 1000,
    timeoutErrorMessage: "请求超时",
  });
}
  • 浏览器端

axios封装 axios封装以及取消发送请求_axios统一封装_04

  • 服务端

axios封装 axios封装以及取消发送请求_axios封装_05

「Axios 配置」

  • AxiosRequestConfig请求属性配置

config

default

Remark

url

请求路径

baseURL

基础请求路径

method

请求方法

params

请求参数

paramsSerializer

paramsSerializer 是一个负责 params 序列化的函数

data

请求体

headers

头部属性

timeout

超时设置

timeoutErrorMessage

超时提示语

responseType

json

响应类型

responseEncoding

utf8

响应编码格式

withCredentials

跨域请求时是否需要使用凭证

maxContentLength

允许的响应内容的最大尺寸

maxRedirects

5

在 node.js 中 follow 的最大重定向数目

httpAgent

在 node.js 中用于定义在执行 http 和 https 时使用的自定义代理

httpsAgent

在 node.js 中用于定义在执行 http 和 https 时使用的自定义代理

cancelToken

cancelToken 指定用于取消请求的 cancel token

xsrfCookieName

XSRF-TOKEN

xsrfHeaderName

X-XSRF-TOKEN

transformRequest

transformRequest 允许在向服务器发送前,修改请求数据

只能用在 ‘PUT’, ‘POST’ 和 ‘PATCH’ 这几个请求方法

transformResponse

transformResponse 在传递给 then/catch 前,允许修改响应数据

validateStatus

onUploadProgress

上传进度回调函数

onDownloadProgress

下载进度回调函数

「Axios 统一封装」

注: 当我们开发前后台分离项目时,我们会调用很多服务,如果每次都基础方式调用,不方便管理而且存在代码重复,也不方便公共操作的实现,比如每次调用服务前组装服务请求头信息等等

  • 方式一
import axios from "axios";

const config = {
  baseURL: "http://localhost:3001",
  timeout: 50000,
};

// 创建axios实例
var instance = axios.create();
//超时设置
instance.defaults.timeout = config.timeout;
instance.defaults.baseURL = config.baseURL;
//设置form头属性
instance.defaults.headers.post["Content-Type"] =
  "application/x-www-form-urlencoded;charset=UTF-8";

//request 拦截器,统一处理请求对象
instance.interceptors.request.use(
  (config) => {
    // 统一请求头处理
    const token = "token";
    token && (config.headers.Authorization = token);
    return config;
  },
  (error) => {
    return Promise.error(error);
  }
);
//response 拦截器,统一处理响应对象
instance.interceptors.response.use(
  (response) => {
    if (response.status === 200) {
      return Promise.resolve(response.data);
    } else {
      return Promise.reject(response);
    }
  },
  // 服务器状态码不是200的情况
  (error) => {
    if (error.response && error.response.status) {
      switch (error.response.status) {
        // 401: 未登录
        // 未登录则跳转登录页面,并携带当前页面的路径
        // 在登录成功后返回当前页面,这一步需要在登录页操作。
        case 401:
          console.info("跳转登录页");
          break;
        // 403 token过期
        // 登录过期对用户进行提示
        // 清除本地token和清空vuex中token对象
        // 跳转登录页面
        case 403:
          console.info("跳转登录页登陆过期");
          // 清除token
          localStorage.removeItem("token");
          // store.commit('loginSuccess', null);
          // 跳转登录页面,并将要浏览的页面fullPath传过去,登录成功后跳转需要访问的页面
          setTimeout(() => {
            console.info("跳转过期");
          }, 1000);
          break;
        // 404请求不存在
        case 404:
          console.info("404");
          break;
        // 其他错误,直接抛出错误提示
        default:
          console.info("其他错误");
      }
      return Promise.reject(error.response);
    }else {
    return Promise.reject(error.message);
    }
  }
);
export default instance;

请求效果

axios封装 axios封装以及取消发送请求_axios封装_06

  • 方式二
import axios from "axios";
import { cookie } from "levenx-utils";

const defaultConfig = {
  baseURL: {
    normal: "http://localhost:3001",
    mock: "https://www.easy-mock.com/mock/5d2340fc05020b09e165b709/bookService",
  },
  timeout: 5000,
  method: "get",
  silent: true,
  isMock: false,
};

export default class Axios {
  static request(options) {
    options = Object.assign({}, defaultConfig, options);
    const {
      isMock,
      baseURL,
      timeout,
      url,
      method,
      params,
      data,
      //不显示loading
      silent,
    } = options;

    let loading;
    if (!silent) {
      loading = document.getElementById("ajaxLoading");
      if (loading) {
        loading.style.display = "block";
      }
    }
    return axios({
      url,
      method,
      baseURL: isMock ? baseURL["mock"] : baseURL["normal"],
      timeout,
      params,
      data,
      headers: {
        Authorization: cookie.get("authorization"),
      },
    })
      .then((response) => {
        //设置token
        if (response.headers.authorization) {
          cookie.set("authorization", response.headers.authorization);
        }
        if (!silent) {
          loading = loading || document.getElementById("ajaxLoading");
          if (loading) {
            loading.style.display = "none";
          }
        }
        if (response.status === 200) {
          let res = response.data;
          return res;
        } else {
          return response.data;
        }
      })
      .catch((error) => {
        if (!silent) {
          loading = loading || document.getElementById("ajaxLoading");
          if (loading) {
            loading.style.display = "none";
          }
        }
        alert(`提示:${error.message}`);
      });
  }

  static get(url, options = {}) {
    return Axios.request(Object.assign({}, options, { url }));
  }

  static post(url, data, options = {}) {
    return Axios.request(
      Object.assign({}, options, { url, data, method: "post" })
    );
  }
}

请求效果

axios封装 axios封装以及取消发送请求_axios_07

「Axios 高级玩法」

  • 取消请求
import axios from "axios";
const CancelToken = axios.CancelToken;
const source = CancelToken.source();


Axios.post(
      "/levenx/common/post",
      {
        name: "levenx",
      },
      { cancelToken: source.token }
    )
      .then((res) => {
        debugger;
      })
      .catch((err) => {
        alert(err);
        return err;
      });
    debugger;
    source.cancel("取消请求");

axios封装 axios封装以及取消发送请求_axios封装_08

  • 请求重试

对于一个应用来说,如果由于某一个接口请求失败就导致应用崩溃或者给用户提示系统异常,但是我们又没办法避免由于服务不稳定导致的偶尔接口请求失败的情况。

此时可以考虑引入 「请求重试」 功能。

  1. 引入 axios-retry
npm install axios-retry
  1. 小试牛刀
import axios from "axios";
import axiosRetry from "axios-retry";

export function retry() {
  axiosRetry(axios, { retries: 2 });
  return axios.get("http://localhost:3001/levenx/common");
}


axios封装 axios封装以及取消发送请求_axios统一封装_09

由于本地后端服务没有开启,重试的参数retries设置为2,所有发起了3次接口请求。

  • 文件上传
// 上传方法
function upload(url, file) {
  let config = {
    headers: { "Content-Type": "multipart/form-data" },
    baseURL: "http://localhost:3001",
  };
  let data = new FormData();
  data.append("file", file);

  return axios.post(url, data, config);
}

//react.js
class Login extends Component {
  async componentDidMount() {}

	//上传按钮触发上传动作
  upload = async () => {
    const { file } = this.state;
    let result = await upload("/levenx/common/upload", file);
  };
  render() {
    return (
      <div>
        <input
          type='file'
          onChange={(e) => {
          	// 监听文件变化
            this.setState({ file: e.currentTarget.files[0] });
          }}
        />
        <button onClick={this.upload}>上传</button>
      </div>
    );
  }
}

axios封装 axios封装以及取消发送请求_axios封装_10

备注:上传图片后端代码可以参考之前写的博客。ajax图片上传功能实现(点击,拖拽,粘贴)Koa服务端

  • XSRF 防御

CSRF 攻击的原理,就是利用由于浏览器的同源策略对 script 、img、video、audio等嵌入资源不做限制的行为进行跨站请求伪造的。 具体的XSS、CSRF攻击会单独记录,敬请关注。

// xsrf 配置
function get(url) {
  return axios.get(url, {
    method: "get",
    baseURL: "http://localhost:3001",
    params: {},
    timeout: 1000,
    timeoutErrorMessage: "请求超时",
    //xsrf
    withCredentials: true,
    xsrfCookieName: "XSRF-TOKEN",
    xsrfHeaderName: "X-XSRF-TOKEN",
  });
}

//服务端验证
static async home(ctx) {
    const header = ctx.header;
    const cookie = ctx.cookies;
    console.log("x-xsrf-token:", header["x-xsrf-token"]);
    console.log("XSRF-TOKEN", cookie.get("XSRF-TOKEN"));
    return {
      code: 0,
      message: "首页",
    };
  }

服务端效果图

axios封装 axios封装以及取消发送请求_axios统一封装_11

判断是否存在xsrf攻击

判断header中 header[“x-xsrf-token”]cookie.get(“XSRF-TOKEN”) 是否存在并且相等。 比如<img src=“http://localhost:3001/levenx/xsrf”/>也会发起服务,但是header属性并不会存在 x-xsrf-token。

模拟xsrf攻击

<img src='http://localhost:3001/levenx/common' />


axios封装 axios封装以及取消发送请求_ios_12

<img src=‘http://localhost:3001/levenx/common’ />, cookie可以读取,但是header中缺少属性,从而可以防御xsrf。

axios提供快捷的方式设置 header 属性,如开局代码展示。

  • 文件上传进度条onUploadProgress
function upload(url, file) {
  let config = {
    headers: { "Content-Type": "multipart/form-data" },
    baseURL: "http://localhost:3001",
    //上传进度条处理
    onUploadProgress: ({ loaded, total }) => {
      console.log("loaded:", loaded);
      console.log("total:", total);
      console.log("百分比:", (loaded / total) * 100 + "%");
    },
  };
  let data = new FormData();
  data.append("file", file);

  return axios.post(url, data, config);
}

axios封装 axios封装以及取消发送请求_axios统一封装_13

axios封装 axios封装以及取消发送请求_axios文件上传_14