当前位置:   article > 正文

Axios封装多种方式实现以及高级用法详解(文件上传,请求重试,取消请求等)_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
  • 1
  • GET
function get(url) {
  return axios.get(url, {
    method: "get",
    baseURL: "http://localhost:3001",
    params: {},
    timeout: 1000,
    timeoutErrorMessage: "请求超时",
  });
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

正常返回

image-20201110121109237

请求超时

image-20201110121622099
  • POST
function post(url, data) {
  return axios.post(url, data, {
    baseURL: "http://localhost:3001",
    timeout: 1000,
    timeoutErrorMessage: "请求超时",
  });
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 浏览器端
image-20201110122514710
  • 服务端
image-20201110122708218

「Axios 配置」

  • AxiosRequestConfig请求属性配置
configdefaultRemark
url请求路径
baseURL基础请求路径
method请求方法
params请求参数
paramsSerializerparamsSerializer 是一个负责 params 序列化的函数
data请求体
headers头部属性
timeout超时设置
timeoutErrorMessage超时提示语
responseTypejson响应类型
responseEncodingutf8响应编码格式
withCredentials跨域请求时是否需要使用凭证
maxContentLength允许的响应内容的最大尺寸
maxRedirects5在 node.js 中 follow 的最大重定向数目
httpAgent在 node.js 中用于定义在执行 http 和 https 时使用的自定义代理
httpsAgent在 node.js 中用于定义在执行 http 和 https 时使用的自定义代理
cancelTokencancelToken 指定用于取消请求的 cancel token
xsrfCookieNameXSRF-TOKEN
xsrfHeaderNameX-XSRF-TOKEN
transformRequesttransformRequest 允许在向服务器发送前,修改请求数据
只能用在 ‘PUT’, ‘POST’ 和 ‘PATCH’ 这几个请求方法
transformResponsetransformResponse 在传递给 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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76

请求效果

image-20201110145316956

  • 方式二
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" })
    );
  }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87

请求效果

image-20201110144626702

「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("取消请求");
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
image-20201110152650892
  • 请求重试

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

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

  1. 引入 axios-retry
npm install axios-retry
  • 1
  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");
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
image-20201113095920496

由于本地后端服务没有开启,重试的参数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>
    );
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

image-20201110161938744

备注:上传图片后端代码可以参考之前写的博客。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: "首页",
    };
  }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

服务端效果图

image-20201110170121007

判断是否存在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' />
  • 1
image-20201110170352569

<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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

image-20201110180310157


声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/266015
推荐阅读
相关标签
  

闽ICP备14008679号