Axios封装

4 篇文章 0 订阅

Axios封装

 dsa



参考

前言

axios常常被推荐使用进行网络请求,直接使用它其实也很方便,不过当项目扩展,网络接口变多时我们还需要对axios进行二次封装


提示:以下是本篇文章正文内容,下面案例可供参考

准备工作

前端

创建一个react + typescript的脚手架

npx create-react-app ts-demo --template  typescript

下载axios依赖

yarn add axios

后端

选择使用gin框架简单搭建,这里不详细说明

项目创建

go mod init demo

下载gin配置

go get github.com/gin-gonic/gin

代码

package main

import (
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
)

// 解决跨域问题的中间件
func Cors() gin.HandlerFunc {

	return func(c *gin.Context) {
		method := c.Request.Method
		origin := c.Request.Header.Get("Origin")

		if origin != "" {
			c.Header("Access-Control-Allow-Origin", origin)
			c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE")
			c.Header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization")
			c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Cache-Control, Content-Language, Content-Type")
			c.Header("Access-Control-Allow-Credentials", "true")
			c.Header("Access-Control-changeOrigin", "true")
			c.Header("Access-Encoding", "*")

		}
		if method == "OPTIONS" {
			c.AbortWithStatus(http.StatusNoContent)
		}
		c.Next()
	}
}

func DefaultGet(c *gin.Context) {
	time.Sleep(time.Second * 2)

	c.JSON(200, gin.H{
		"Content": "Hello World!",
		"code":    200,
	})
}

func DefaultPost(c *gin.Context) {
	c.String(200, "Hello, world!")
}

func server() {
	router := gin.Default()
	router.Use(Cors())
	gr := router.Group("/api")
	gr.GET("/text", DefaultGet)
	gr.POST("/", DefaultPost)
	router.Run(":8081")
}

func main(){
	server()
}

axos 封装

配置信息

可以在进行编写后面之前先准备一些基础数据,比如状态码、动态的baseURL……

/**
 * 状态码
 */
const ServerCode = {
  SUCCESS: 200,
  CONTINUE: 400,
  WRONG_PARAM: 401,
  WRONG_REQUEST: 402,
  FORBIDDEN: 403,
  WRONG_URL: 404,
  NO_LOGIN: 406,
  TIME_OUT: 408,
  WRONG_REQUEST_MODAL: 410,
  AUTO_SAVE_ERROR_CODE: 411, // 自动暂存code
  PROCESS_ERROR: 412, // 任务进度错误
  WRONG_SERVER: 500,
  WRONG_REALIZE: 501,
  WRONG_GATEWAY: 502,
  BAD_SERVER: 503,
  GATEWAY_TIME_OUT: 504,
  WRONG_VERSION: 505
};

简单编写axios封装

// 进行统一配置
const service: AxiosInstance = axios.create({
  baseURL: "http://localhost:8081/api/",
  // 统一设置超时时间
  timeout: 5000,
  // 允许携带cookie
  withCredentials: true,
})

值得注意的是service(axiosConfig)返回的是一个Promise对象

对request进行统一配置,并避免重复请求

// cancelToken存储
const source: { [key: string]: any } = {};
// 存储接口的url
let requestList: string[] = [];

/**
 * 请求的统一配置
 */
service.interceptors.request.use(
  (config:any) => {
      if(/get/i.test(config.method)){
        config.params = {
          ...config.params,
          _t: Date.now()
        }
        config.headers = {
          'Content-Type': '*',
          ...config.headers,
        }
        const requestIndex = requestList.findIndex(item => item === config.url);
        // 阻止重复请求
        if(requestIndex !== -1){
          source[config.url] && source[config.url]("终止请求");
          requestList.splice(requestIndex, 1);
        }
        // 存储阻止请求的method
        config.cancelToken = new axios.CancelToken(cancel => 
          source[config.url] = cancel
        );
        requestList.push(config.url);
      }
      return config;
    },
  err => {
    // 设置请求出错时做的事
    return Promise.reject(err);
  }
)

统一response设置

/**
 * response拦截器
 */
service.interceptors.response.use(
  (response:any) => {
    const {url, method} = response.config;

    if(/get/i.test(method as Method)){
      const requestIndex = requestList.findIndex(item => item === url);
      // 收到相应后删去记录重复请求记录数组的信息
      if(requestIndex > -1) {
        requestList.splice(requestIndex, 1);
        delete source[url];
      }
    }

    const res = response.data;
    if(res.code === ServerCode.SUCCESS) return res;
    return Promise.reject({data:res, response});
  },
  (error:any) => {
    // 统一错误拦截
    return Promise.reject(error);
  }
)

统一错误处理

/**
 * 统一处理错误
 * @param err 错误对象
 * @returns 
 */
const errorHandel: <R>(err:AxiosError<R>) => Promise<R> = err => {
  if(axios.isCancel(err)) return Promise.reject(err);
  let message = "";
  if(err && err.response) {
    switch(err.response.status) {
      case 302: message = '接口重定向了!';break;
      case 400: message = '参数不正确!';break;
      case 401: message = '您未登录,或者登录已经超时,请先登录!';break;
      case 403: message = '您没有权限操作!'; break;
      case 404: message = `请求地址出错: ${err.response.config.url}`; break; // 在正确域名下
      case 408: message = '请求超时!'; break;
      case 409: message = '系统已存在相同数据!'; break;
      case 500: message = '服务器内部错误!'; break;
      case 501: message = '服务未实现!'; break;
      case 502: message = '网关错误!'; break;
      case 503: message = '服务不可用!'; break;
      case 504: message = '服务暂时无法访问,请稍后再试!'; break;
      case 505: message = 'HTTP版本不受支持!'; break;
      default: message = '异常问题,请联系管理员!'; break
    }
    if(err?.message.includes('timeout')) message = '请求超时!';
    if(err?.message.includes('Network Error')) message = '网络异常!';
  }
  console.log(message)
  return Promise.reject(err);
}

统一处理请求返回的结果

/**
 * 统一处理请求返回结果
 * @param axiosObj 
 * @returns 
 */
const myResponse: <R>(axiosObj: any) => Promise<R> = axiosObj => {
  return axiosObj.then((res:any) => res).catch((err: any) => errorHandel(err));
}

这里需要结合Promise进行理解,要注意myResponse的返回值是一个Promise对象

对get、post、put、delete请求封装

/**
 * 四种请求方式
 * @param url       接口地址
 * @param data      接口参数
 * @param headers   接口所需header配置
 * @param gateWay   用户验证信息
 * @returns 
 */

export const get: <R>(req:any) => Promise<R> = ({url, data, headers, gateWay }) => 
  myResponse(
    service.get(url, {
      params: data,
      headers,
      // @ts-ignore
      gateWay,
      paramsSerializer: params => {
        return qs.stringify(params, { indices: false });
      }
    })
  )

export const post: <R>(req:any) => Promise<R> = ({url, data, headers, gateWay }) => 
  // @ts-ignore
  myResponse(service.post(url, data, {headers, gateWay}))

export const put: <R>(req:any) => Promise<R> = ({ url, data, headers, gateWay }) =>
  // @ts-ignore
  myResponse(service.put(url, data, { headers, gateWay }));
export const del: <R>(req:any) => Promise<R> = ({ url, data, headers, gateWay }) =>
  // @ts-ignore
  myResponse(service.delete(url, { data, headers, gateWay }));

使用

直接修改react项目的中app.tsx中的内容, 设置一个按钮然后触发请求

代码

import './App.css';

import axios from "axios";
import {get} from "./Axios/http" ;


function App() {
  return (
    <div className="App">
      <header className="App-header">
        <button 
          onClick={() => {
            // 使用封装的get方法
            get({url:"text"}).then((res: any) => console.log(res)).catch((err: any) => console.log(err))
            
            // 直接使用axios
            // axios({
            //   method: "get",
            //   timeout: 5000,
            //   url: "http://localhost:8081/api/text?_t=1656124014230",
            //   headers: {
            //     'Content-Type': '*',
            //   },
            // }).then(res => console.log(res)).catch((err: any) => console.log(err))
          }}
          >确定</button>
      </header>
    </div>
  );
}

export default App;

可以看到封装后的使用起来比直接使用axios更加方便,而且其中内置了关于重复请求、错误处理等操作。

封装部分的完整代码

import qs from 'qs';
import axios, {AxiosInstance, Method, AxiosError} from "axios";

/**
 * 状态码
 */
const ServerCode = {
  SUCCESS: 200,
  CONTINUE: 400,
  WRONG_PARAM: 401,
  WRONG_REQUEST: 402,
  FORBIDDEN: 403,
  WRONG_URL: 404,
  NO_LOGIN: 406,
  TIME_OUT: 408,
  WRONG_REQUEST_MODAL: 410,
  AUTO_SAVE_ERROR_CODE: 411, // 自动暂存code
  PROCESS_ERROR: 412, // 任务进度错误
  WRONG_SERVER: 500,
  WRONG_REALIZE: 501,
  WRONG_GATEWAY: 502,
  BAD_SERVER: 503,
  GATEWAY_TIME_OUT: 504,
  WRONG_VERSION: 505
};



// 进行统一配置
const service: AxiosInstance = axios.create({
  baseURL: "http://localhost:8081/api/",
  timeout: 5000,
  // 允许携带cookie
  withCredentials: true,
})

// cancelToken存储
const source: { [key: string]: any } = {};
// 存储接口的url
let requestList: string[] = [];

/**
 * 请求的统一配置
 */
service.interceptors.request.use(
  (config:any) => {
      if(/get/i.test(config.method)){
        config.params = {
          ...config.params,
          _t: Date.now()
        }
        config.headers = {
          'Content-Type': '*',
          ...config.headers,
        }
        const requestIndex = requestList.findIndex(item => item === config.url);
        // 阻止重复请求
        if(requestIndex !== -1){
          source[config.url] && source[config.url]("终止请求");
          requestList.splice(requestIndex, 1);
        }
        config.cancelToken = new axios.CancelToken(cancel => 
          source[config.url] = cancel
        );

        requestList.push(config.url);
      }
      return config;
    },
  err => {
    // 设置请求出错时做的事
    return Promise.reject(err);
  }
)

/**
 * response拦截器
 */
service.interceptors.response.use(
  (response:any) => {
    const {url, method} = response.config;

    if(/get/i.test(method as Method)){
      const requestIndex = requestList.findIndex(item => item === url);
      if(requestIndex > -1) {
        requestList.splice(requestIndex, 1);
        delete source[url];
      }
    }

    const res = response.data;
    if(res.code === ServerCode.SUCCESS) return res;
    return Promise.reject({data:res, response});
  },
  (error:any) => {
    // 统一错误拦截
    return Promise.reject(error);
  }
)

/**
 * 统一处理错误
 * @param err 错误对象
 * @returns 
 */
const errorHandel: <R>(err:AxiosError<R>) => Promise<R> = err => {
  if(axios.isCancel(err)) return Promise.reject(err);
  let message = "";
  if(err && err.response) {
    switch(err.response.status) {
      case 302: message = '接口重定向了!';break;
      case 400: message = '参数不正确!';break;
      case 401: message = '您未登录,或者登录已经超时,请先登录!';break;
      case 403: message = '您没有权限操作!'; break;
      case 404: message = `请求地址出错: ${err.response.config.url}`; break; // 在正确域名下
      case 408: message = '请求超时!'; break;
      case 409: message = '系统已存在相同数据!'; break;
      case 500: message = '服务器内部错误!'; break;
      case 501: message = '服务未实现!'; break;
      case 502: message = '网关错误!'; break;
      case 503: message = '服务不可用!'; break;
      case 504: message = '服务暂时无法访问,请稍后再试!'; break;
      case 505: message = 'HTTP版本不受支持!'; break;
      default: message = '异常问题,请联系管理员!'; break
    }
    if(err?.message.includes('timeout')) message = '请求超时!';
    if(err?.message.includes('Network Error')) message = '网络异常!';
  }
  console.log(message)
  return Promise.reject(err);
}

/**
 * 统一处理请求返回结果
 * @param axiosObj 
 * @returns 
 */
const myResponse: <R>(axiosObj: any) => Promise<R> = axiosObj => {
  return axiosObj.then((res:any) => res).catch((err: any) => errorHandel(err));
}


/**
 * 四种请求方式
 * @param url       接口地址
 * @param data      接口参数
 * @param headers   接口所需header配置
 * @param gateWay   用户验证信息
 * @returns 
 */

export const get: <R>(req:any) => Promise<R> = ({url, data, headers, gateWay }) => 
  myResponse(
    service.get(url, {
      params: data,
      headers,
      // @ts-ignore
      gateWay,
      paramsSerializer: params => {
        return qs.stringify(params, { indices: false });
      }
    })
  )

export const post: <R>(req:any) => Promise<R> = ({url, data, headers, gateWay }) => 
  // @ts-ignore
  myResponse(service.post(url, data, {headers, gateWay}))

export const put: <R>(req:any) => Promise<R> = ({ url, data, headers, gateWay }) =>
  // @ts-ignore
  myResponse(service.put(url, data, { headers, gateWay }));
export const del: <R>(req:any) => Promise<R> = ({ url, data, headers, gateWay }) =>
  // @ts-ignore
  myResponse(service.delete(url, { data, headers, gateWay }));

export default service;



  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值