详细自定义封装Axios请求库,你还不会二次封装吗?(一)

简介: 使用Vue的时候,Axios几乎已经是必用的请求库了,但是为了更方便搭配项目使用,很多开发者会选择二次封装,Vue3就很多人选择二次封装elementPlus,那其实,Axios我们也是经常会去封装的。

使用Vue的时候,Axios几乎已经是必用的请求库了,但是为了更方便搭配项目使用,很多开发者会选择二次封装,Vue3就很多人选择二次封装elementPlus,那其实,Axios我们也是经常会去封装的。

封装有什么好处呢?

首先,封装的目的主要是便于全局化使用。

比如全局设置超时时间,固定接口的baseURL,实现请求拦截操作与响应拦截操作。

那现在我就来展示一下我经常使用的封装套路。

封装功能

首先是功能上的封装,我们新建一个js文件,我这里叫request.js

首先我们先导入axios和qs两个模块。

为什么要使用qs模块?

ajax请求的get请求是通过URL传参的(以?和&符连接),而post大多是通过json传参的。

qs是一个库。里面的stringify方法可以将一个json对象直接转为(以?和&符连接的形式)。

在开发中,发送请求的入参大多是一个对象。在发送时,如果该请求为get请求,就需要对参数进行转化。使用该库,就可以自动转化,而不需要手动去拼接

然后我这里还会用一个弹出层UI,我这里用elementUI,你也可以选择其他UI,灵活变通。但是最好不要全引入,单个引入弹出层组件就可以。

// 导入axios
import axios from 'axios'
//导入QS
import qs from 'qs'
// 使用element-ui Message用以消息提醒
import { Message} from 'element-ui';

导入之后,我们创建一个axios的实例,可以理解为对象吧。

// 创建新的axios实例
const service = axios.create({
  // 公共接口(暂未配置,预计写死)
  baseURL: "http://localhost:8081/api",
  // 超时时间 单位是ms
  timeout: 20 * 1000,
})

网络异常,图片无法展示
|

Axios的官方文档也说明了创建实例的方法。

然后里面有一些配置项,比如baseURL,超时时间等,官网还要很多的配置,这里就不多说了。

此时这个实例service就是我们要用的axios了,你就当他是axios的对象。

请求拦截器

网络异常,图片无法展示
|

文档也提供了拦截器设置方法,我们调用这个方法,自己封装一下请求与响应拦截。

// 添加请求拦截器
axios.interceptors.request.use(function (config) {
    // 在发送请求之前做些什么
    return config;
  }, function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
  });

官方的拦截器是这样的。

我这里喜欢用箭头函数,所以是这样的:

// 请求拦截器
service.interceptors.request.use(config => {
  return config
}, error => {
  Promise.reject(error)
})

这里携带的config是一个数据配置项,每次发送请求后,整个axios的东西都会被我们获取到,然后我们这使用config接收。

那既然这是一个axios的数据包,那我们就可以添加修改里面的数据。

我们看看它源码对应的代码段,是TS写的,是一个泛型对象,对象中包含了一些设置参数。

网络异常,图片无法展示
|

图有些模糊,我贴个代码:

export interface AxiosRequestConfig<D = any> {
  url?: string;
  method?: Method;
  baseURL?: string;
  transformRequest?: AxiosRequestTransformer | AxiosRequestTransformer[];
  transformResponse?: AxiosResponseTransformer | AxiosResponseTransformer[];
  headers?: AxiosRequestHeaders;
  params?: any;
  paramsSerializer?: (params: any) => string;
  data?: D;
  timeout?: number;
  timeoutErrorMessage?: string;
  withCredentials?: boolean;
  adapter?: AxiosAdapter;
  auth?: AxiosBasicCredentials;
  responseType?: ResponseType;
  xsrfCookieName?: string;
  xsrfHeaderName?: string;
  onUploadProgress?: (progressEvent: any) => void;
  onDownloadProgress?: (progressEvent: any) => void;
  maxContentLength?: number;
  validateStatus?: ((status: number) => boolean) | null;
  maxBodyLength?: number;
  maxRedirects?: number;
  socketPath?: string | null;
  httpAgent?: any;
  httpsAgent?: any;
  proxy?: AxiosProxyConfig | false;
  cancelToken?: CancelToken;
  decompress?: boolean;
  transitional?: TransitionalOptions;
  signal?: AbortSignal;
  insecureHTTPParser?: boolean;
}

那我们就可以设置这些,至于这些配置项都是什么,我们可以前往官方文档查看。

网络异常,图片无法展示
|

在里面对基本上要操作的数据字段都写了注释。

请求拦截转换JSON数据:

config.data = qs.stringify(config.data);

用qs转化一下,原因前面已经说了。

设置固定请求头:

config.headers = {
      //配置请求头
      'Content-Type':'application/x-www-form-urlencoded' 
  }

携带参数/Token:

if (localStorage.getItem('token')) {
    //携带token到axios参数
    config.headers.Authorization = '固定携带的头部';
    config.params = {
      //固定携带参数
    }
  }

这里是从浏览器内存读取token,你可以选择携带到头部。

当然,你也可以携带其他数据,也可以在config.params中携带一些其他参数,每次请求都会默认携带到后端。

你也可以选择在cookie里面获取:

const token = getCookie('名称');//这里取token之前,需要先拿到token,存一下
  if(token){
      config.params = {'token':token} //如果要求携带在参数中
      config.headers.token= token; //如果要求携带在请求头中
  }

最后,不要忘记return config,不然设置的字段不会生效。

然后我们Axios因为是基于Promise的,所以我们最后可以使用Promise.reject捕捉他的错误信息。

Promise.reject会在error中返回一个Promise错误对象对象。

这里不懂请查阅Promise相关资料。

那为了方便查看,我就整个拦截器代码放出来了:

// 请求拦截器
service.interceptors.request.use(config => {
  //发请求前做的一些处理,数据转化,配置请求头,设置token,设置loading等,根据需求添加
  config.data = qs.stringify(config.data); //json数据转化
  config.headers = {
      'Content-Type':'application/x-www-form-urlencoded' //配置请求头
  }
  //注意使用token的时候需要引入cookie方法或者用本地localStorage等方法,推荐js-cookie
  //判断localStorage是否存在token
  if (localStorage.getItem('token')) {
    //携带token到axios参数
    config.headers.Authorization = '固定携带的头部';
    config.params = {
      //固定携带参数
    }
  }
  // const token = getCookie('名称');//这里取token之前,需要先拿到token,存一下
  // if(token){
  //     config.params = {'token':token} //如果要求携带在参数中
  //     config.headers.token= token; //如果要求携带在请求头中
  // }
  return config
}, error => {
  Promise.reject(error)
})

网络异常,图片无法展示
|

这部分就是捕捉错误的代码。

响应拦截器

响应拦截器将会搭配elementUI的弹出层提示组件,当返回响应报错时,自动弹出提示,优化用户体验。

官方是这样写的:

// 添加响应拦截器
axios.interceptors.response.use(function (response) {
    // 2xx 范围内的状态码都会触发该函数。
    // 对响应数据做点什么
    return response;
  }, function (error) {
    // 超出 2xx 范围的状态码都会触发该函数。
    // 对响应错误做点什么
    return Promise.reject(error);
  });

那我们还是使用箭头函数来写,这里我先给出所以代码,在分段解析。

service.interceptors.response.use(response => {
    console.log("进入响应拦截器");
  //接收到响应数据并成功后的一些共有的处理,关闭loading等  
  return response
}, error => {
   /***** 接收到异常响应的处理开始 *****/
  if (error && error.response) {
    // 根据响应码具体处理
    switch (error.response.status) {
      case 400:
        error.message = '错误请求'
        break;
      case 401:
        error.message = '未授权,请重新登录'
        break;
      case 403:
        error.message = '拒绝访问'
        break;
      case 404:
        error.message = '请求错误,未找到该资源'
        window.location.href = "/NotFound"
        break;
      case 405:
        error.message = '请求方法未允许'
        break;
      case 408:
        error.message = '请求超时'
        break;
      case 500:
        error.message = '服务器端出错'
        break;
      case 501:
        error.message = '网络未实现'
        break;
      case 502:
        error.message = '网络错误'
        break;
      case 503:
        error.message = '服务不可用'
        break;
      case 504:
        error.message = '网络超时'
        break;
      case 505:
        error.message = 'http版本不支持该请求'
        break;
      default:
        error.message = `连接错误${error.response.status}`
    }
  } else {
    // 超时处理
    if (JSON.stringify(error).includes('timeout')) {
      Message.error('服务器响应超时,请刷新当前页')
    }
    error.message = '连接服务器失败'
  }
  Message.error(error.message)
  /***** 处理结束 *****/
  return Promise.resolve(error.response)
})

这里有一个返回的参数response

service.interceptors.response.use(response => {
    console.log("进入响应拦截器");
  //接收到响应数据并成功后的一些共有的处理,关闭loading等  
  return response
},

这个也是Promise的,所以,我们在正常运行的时候,会正常进入方法,所以返回接收的数据。

如果出现错误,他是不会进入到上面的方法的,而是进入error

error => {
   /***** 接收到异常响应的处理开始 *****/
  if (error && error.response) {
    // 根据响应码具体处理
    switch (error.response.status) {
      case 400:
        error.message = '错误请求'
        break;
      case 401:
        error.message = '未授权,请重新登录'
        break;
      case 403:
        error.message = '拒绝访问'
        break;
      case 404:
        error.message = '请求错误,未找到该资源'
        window.location.href = "/NotFound"
        break;
      case 405:
        error.message = '请求方法未允许'
        break;
      case 408:
        error.message = '请求超时'
        break;
      case 500:
        error.message = '服务器端出错'
        break;
      case 501:
        error.message = '网络未实现'
        break;
      case 502:
        error.message = '网络错误'
        break;
      case 503:
        error.message = '服务不可用'
        break;
      case 504:
        error.message = '网络超时'
        break;
      case 505:
        error.message = 'http版本不支持该请求'
        break;
      default:
        error.message = `连接错误${error.response.status}`
    }else {
    // 超时处理
    if (JSON.stringify(error).includes('timeout')) {
      Message.error('服务器响应超时,请刷新当前页')
    }
    error.message = '连接服务器失败'
  }
  Message.error(error.message)
  /***** 处理结束 *****/
  return Promise.resolve(error.response)
})

也就是进入以上代码。

那首先进入这个方法,我们先来一个判断。

if (error && error.response) {
    //错误码判断
}else{
    //超时处理
}

这个判断,我去除中间的部分,先看这个判断。

如果有error对象,并且error对象有response参数时,我们此时就会确定这是请求状态错误。

为什么呢?因为error.response中的status会返回浏览器爆出的状态码。

那如果没有报状态码,那就说明非直接的错误,那就可能是超时了,我们在else中进一步处理。

状态码处理

那我们还是先看直接错误处理:

网络异常,图片无法展示
|

我们获取到状态码,根据不同状态码弹出不同错误提示,这里我们将错误提示文字报错到这个error中。

这里还只是保存错误信息,还没有调用elementUI弹出层哦!

是不是很方便呢?

进一步处理
else {
    // 超时处理
    if (JSON.stringify(error).includes('timeout')) {
      Message.error('服务器响应超时,请刷新当前页')
    }
    error.message = '连接服务器失败'
}

那如果没有状态码,基本上就是超时,获取其他问题。

那我们if判断一下看看是否超时,先使用JSON.stringify将对象转化为字符串。

includes方法是用于判断字符串中有没有对应字符串。

然后使用includes判断有没有timeout这个字符串,有就是超时了。

没有我们就默认给他抛出一个error.message = '连接服务器失败'

弹出提示:

不要忘了,我们还只是保存错误提示的字符串,没有调用elementUI的弹出层组件,我们最后调用一下。

Message.error(error.message)

调用后不要忘了返回参数,我们需要使用Promise.resolve来返回一个error.response

Promise.resolve作用是将参数转为Promise对象。

具体请自行查阅相关资料,不懂就按照这个来,官方也是这样的。

相关文章
|
28天前
|
资源调度 JavaScript 前端开发
vue-element-admin 综合开发四:axios安装和封装、mock安装/学习/使用
这篇文章是关于如何在Vue项目中使用axios进行网络请求、二次封装axios以及使用mockjs模拟响应数据的教程。
69 1
vue-element-admin 综合开发四:axios安装和封装、mock安装/学习/使用
|
9天前
|
JavaScript 前端开发 Java
SpringBoot项目的html页面使用axios进行get post请求
SpringBoot项目的html页面使用axios进行get post请求
28 2
|
24天前
|
Python
axios的get请求传入数组参数
【10月更文挑战第11天】 当使用 `axios` 发送包含数组参数的 GET 请求时,默认的序列化方式可能与后端(如 Django)不兼容,导致无法正确获取数组参数。解决方案是通过 `paramsSerializer` 指定自定义序列化函数,或使用 `qs` 库来格式化数组参数,确保前后端一致。示例代码展示了如何使用 `qs` 库设置 `arrayFormat` 为 `&quot;repeat&quot;`,以符合 Django 的解析要求。
22 2
|
28天前
vue3 + Ts 中 使用 class 封装 axios
【10月更文挑战第8天】
96 1
|
1月前
|
JSON JavaScript 前端开发
axios的post请求,数据为什么要用qs处理?什么时候不用?
axios的post请求,数据为什么要用qs处理?什么时候不用?
|
23天前
|
前端开发 JavaScript API
自己动手封装axios通用方法并上传至私有npm仓库:详细步骤与实现指南
自己动手封装axios通用方法并上传至私有npm仓库:详细步骤与实现指南
69 0
|
24天前
|
前端开发 JavaScript UED
axios取消请求CancelToken的原理解析及用法示例
axios取消请求CancelToken的原理解析及用法示例
73 0
|
1月前
|
资源调度 JavaScript
|
3月前
|
JavaScript 前端开发
【Vue面试题二十五】、你了解axios的原理吗?有看过它的源码吗?
这篇文章主要讨论了axios的使用、原理以及源码分析。 文章中首先回顾了axios的基本用法,包括发送请求、请求拦截器和响应拦截器的使用,以及如何取消请求。接着,作者实现了一个简易版的axios,包括构造函数、请求方法、拦截器的实现等。最后,文章对axios的源码进行了分析,包括目录结构、核心文件axios.js的内容,以及axios实例化过程中的配置合并、拦截器的使用等。
【Vue面试题二十五】、你了解axios的原理吗?有看过它的源码吗?
|
1月前
|
缓存 JavaScript 搜索推荐