axios封装方案

前端项目都需要封装接口,本文以 axios 请求为例,设计一套模版,主要功能包括请求拦截、响应拦截、错误处理、多接口文件统一封包。所有代码给予 ts 编写

项目代码结构推荐

src
  |
  |- index.ts
  |- server.ts
  |- tool.ts
  |- path
        |
        |- user.ts
        |- other.ts

下面进行分别介绍

业务处理函数:src/api/tool.ts

主要负责请求拦截、错误处理等

const handleRequestHeader = (config) => {
 config['xxxx'] = 'xxx'
 
 return config
}

const handleAuth = (config) => {
 config.header['token'] = localStorage.getItem('token') || token || ''
 return config
}
const handleNetworkError = (errStatus) => {
    let errMessage = '未知错误'
    if (errStatus) {
        switch (errStatus) {
            case 400:
                errMessage = '错误的请求'
                break
            case 401:
                errMessage = '未授权,请重新登录'
                break
            case 403:
                errMessage = '拒绝访问'
                break
            case 404:
                errMessage = '请求错误,未找到该资源'
                break
            case 405:
                errMessage = '请求方法未允许'
                break
            case 408:
                errMessage = '请求超时'
                break
            case 500:
                errMessage = '服务器端出错'
                break
            case 501:
                errMessage = '网络未实现'
                break
            case 502:
                errMessage = '网络错误'
                break
            case 503:
                errMessage = '服务不可用'
                break
            case 504:
                errMessage = '网络超时'
                break
            case 505:
                errMessage = 'http版本不支持该请求'
                break
            default:
                errMessage = `其他连接错误 --${errStatus}`
        }
    } else {
        errMessage = `无法连接到服务器!`
    }

    message.error(errMessage)
}

const handleAuthError = (errno) => {
 const authErrMap: any = {
   '10031': '登录失效,需要重新登录', // token 失效
   '10032': '您太久没登录,请重新登录~', // token 过期
   '10033': '账户未绑定角色,请联系管理员绑定角色',
   '10034': '该用户未注册,请联系管理员注册用户',
   '10035': 'code 无法获取对应第三方平台用户',
   '10036': '该账户未关联员工,请联系管理员做关联',
   '10037': '账号已无效',
   '10038': '账号未找到',
 }
 
 if (authErrMap.hasOwnProperty(errno)) {
  message.error(authErrMap[errno])
  // 授权错误,登出账户
  logout()
  return false
 }

 return true
}

const handleGeneralError = (errno, errmsg) => {
 if (err.errno !== '0') {
  meessage.error(err.errmsg)
  return false
 }

 return true
}

通用操作封装:src/api/server.ts

主要负责axios 的请求主体,支持 get、post 请求

import axios from 'axios'
import { message } from 'antd'

import {
 handleChangeRequestHeader,
 handleConfigureAuth,
 handleAuthError,
 handleGeneralError,
 handleNetworkError
} from './tools'

type Fn = (data: FcResponse<any>) => unknown

interface IAnyObj {
    [index: string]: unknown
}

interface FcResponse<T> {
    errno: string
    errmsg: string
    data: T
}

axios.interceptors.request.use((config) => {
  config = handleChangeRequestHeader(config)
 config = handleConfigureAuth(config)
 return config
})

axios.interceptors.response.use(
    (response) => {
        if (response.status !== 200) return Promise.reject(response.data)
        handleAuthError(response.data.errno)
        handleGeneralError(response.data.errno, response.data.errmsg)
        return response
    },
    (err) => {
        handleNetworkError(err.response.status)
        Promise.reject(err.response)
    }
)

export const Get = <T,>(url: string, params: IAnyObj = {}, clearFn?: Fn): Promise<[any, FcResponse<T> | undefined]> =>
  new Promise((resolve) => {
    axios
      .get(url, { params })
      .then((result) => {
        let res: FcResponse<T>
        if (clearFn !== undefined) {
          res = clearFn(result.data) as unknown as FcResponse<T>
        } else {
          res = result.data as FcResponse<T>
        }
        resolve([null, res as FcResponse<T>])
      })
      .catch((err) => {
        resolve([err, undefined])
      })
  })

export const Post = <T,>(url: string, data: IAnyObj, params: IAnyObj = {}): Promise<[any, FcResponse<T> | undefined]> => {
  return new Promise((resolve) => {
    axios
      .post(url, data, { params })
      .then((result) => {
        resolve([null, result.data as FcResponse<T>])
      })
      .catch((err) => {
        resolve([err, undefined])
      })
  })
}

统一调用点:src/api/index.ts

各种具体的接口文件的封包文件,业务逻辑中使用接口均从此处引入即可

import { userApi } from "./path/user"
import { shoporderApi } from "./path/shoporder"

export const api = {
 ...userApi,
 ...shoporderApi
}

接口:src/api/path/user.ts | src/api/path/shoporder.ts

用来使用封装好的 axios 接口来实现具体的业务逻辑

import { Get } from "../server"

export function getUserInfo(id) { ... }

export function getUserName(id) { ... }

export const userApi = {
 getUserInfo,
 getUserName
}

本作品采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 (CC BY-NC-ND 4.0) 进行许可。