基于axios的http请求封装,支持数据缓存

使用场景

在前端开发过程中,使用http/https请求到服务端获取数据基本是必备场景了。

在这里使用tsaxiosantdnotification做了接口请求封装,可根据实际需求再做修改。

封装中包含基于token的登录验证,对请求响应的错误处理,以及对重复请求的缓存策略。

封装过程

创建axios实例

ts 复制代码
const BASE_URL = '/api' // 根据实际需求更改

const http: AxiosInstance = axios.create({
  baseURL: BASE_URL,
  timeout: 10000,
  headers: { 'Content-Type': 'application/json' },
})

使用拦截器做请求及响应的数据处理

请求拦截器

请求拦截器中主要处理用户登录token,如果能够获取到token,则将token传到请求headers对应字段中。

ts 复制代码
http.interceptors.request.use(
  (config) => {
    // 这里可以根据是否需要清除空白参数做调整
    if (config.params) config.params = deleteNullParams(config.params)
    if (config.data) config.data = deleteNullParams(config.data)

    // 如需登录验证,可以在这里添加headers的token验证
    const token = useSystemStore.getState().token
    if (token) config.headers['Authorization'] = token

    return config
  },
  (error) => {
    return Promise.reject(error)
  }
)

function deleteNullParams(params: { [key: string]: unknown }) {
  for (const key in params) {
    if (params[key] === null || params[key] === undefined || params[key] === '') {
      delete params[key]
    }
  }
  return params
}

响应拦截器

响应拦截器主要处理请求响应错误数据。包含http请求本身的错误以及服务返回数据内的自定义数据错误。

ts 复制代码
// 响应数据结构定义
export interface ApiRes<T> {
  code: number
  msg: string
  data: T
}

http.interceptors.response.use(
  <T>(response: AxiosResponse<ApiRes<T>>) => {
    // 响应 status 401 表示登录token验证失败,需要清除token并跳转到登录页面
    if (response.status === 401) {
      useSystemStore.getState().clear()
      location.href = '/login'
      return Promise.reject('Unauthorized access, please login again.')
    }

    // 响应 status 不为 200 表示请求失败
    if (response.status !== 200) {
      showNotify()
      return Promise.reject(response.statusText)
    }

    // 接口数据返回自定义错误处理
    const { code, data, msg } = response.data

    if (code !== 200) {
      showNotify(code, msg)
      return Promise.reject(msg)
    }

    return data
  },
  (error) => {
    showNotify(error.code, error.message)
    return Promise.reject(error.message)
  }
)

请求缓存

请求缓存主要是解决重复请求问题,同样一个请求如果连续发起多次,第一次会正常请求,并将请求的返回Promise保存到缓存中,后面再发起请求时,如果第一次发起的请求还没有返回数据(缓存还在),则将缓存直接作为该次请求的返回数据,无需发起重复请求。

ts 复制代码
const resPromiseCache = new Map<string, Promise<unknown>>()

const cachedRequest = <T>(config: AxiosRequestConfig) => {
  const key = getUniKey(config)

  if (resPromiseCache.has(key)) return resPromiseCache.get(key) as Promise<T>

  const promise = http.request<T>(config)
  resPromiseCache.set(key, promise)
  promise.finally(() => resPromiseCache.delete(key))
  return promise as Promise<T>
}

function getUniKey(config: AxiosRequestConfig): string {
  const { method, url, params, data } = config
  return `${method}-${url}-${JSON.stringify(params)}-${JSON.stringify(data)}`
}

常用请求方法封装

这里封装了常用的GETPOSTPUTDELETE快捷请求函数

ts 复制代码
export const get = <T>(url: string, params?: object): Promise<T> => {
  return cachedRequest<T>({ method: 'get', url, params })
}

export const post = <T>(url: string, data?: object): Promise<T> => {
  return cachedRequest<T>({ method: 'post', url, data })
}

export const put = <T>(url: string, data?: object): Promise<T> => {
  return cachedRequest<T>({ method: 'put', url, data })
}

export const del = <T>(url: string, params?: object, data?: object): Promise<T> => {
  return cachedRequest<T>({ method: 'delete', url, params, data })
}

完整示例

ts 复制代码
import axios from 'axios'
import type { AxiosInstance, AxiosResponse, AxiosRequestConfig } from 'axios'
import { notification } from 'antd'
import useSystemStore from '@/stores/system'

export interface ApiRes<T> {
  code: number
  msg: string
  data: T
}

const BASE_URL = '/api'

const resPromiseCache = new Map<string, Promise<unknown>>()

const http: AxiosInstance = axios.create({
  baseURL: BASE_URL,
  timeout: 10000,
  headers: { 'Content-Type': 'application/json' },
})

http.interceptors.request.use(
  (config) => {
    if (config.params) config.params = deleteNullParams(config.params)
    if (config.data) config.data = deleteNullParams(config.data)

    const token = useSystemStore.getState().token
    if (token) config.headers['Authorization'] = token

    return config
  },
  (error) => {
    return Promise.reject(error)
  }
)

http.interceptors.response.use(
  <T>(response: AxiosResponse<ApiRes<T>>) => {
    if (response.status === 401) {
      useSystemStore.getState().clear()
      location.href = '/login'
      return Promise.reject('Unauthorized access, please login again.')
    }

    if (response.status !== 200) {
      showNotify()
      return Promise.reject(response.statusText)
    }

    const { code, data, msg } = response.data

    if (code !== 200) {
      showNotify(code, msg)
      return Promise.reject(msg)
    }

    return data
  },
  (error) => {
    showNotify(error.code, error.message)
    return Promise.reject(error.message)
  }
)

const cachedRequest = <T>(config: AxiosRequestConfig) => {
  const key = getUniKey(config)

  if (resPromiseCache.has(key)) return resPromiseCache.get(key) as Promise<T>

  const promise = http.request<T>(config)
  resPromiseCache.set(key, promise)
  promise.finally(() => resPromiseCache.delete(key))
  return promise as Promise<T>
}

const showNotify = (status?: number, description = 'Server Error !') => {
  notification.error({
    message: `Request Error ${status ? status : ''}`,
    description,
  })
}

export default http

export const get = <T>(url: string, params?: object): Promise<T> => {
  return cachedRequest<T>({ method: 'get', url, params })
}

export const post = <T>(url: string, data?: object): Promise<T> => {
  return cachedRequest<T>({ method: 'post', url, data })
}

export const put = <T>(url: string, data?: object): Promise<T> => {
  return cachedRequest<T>({ method: 'put', url, data })
}

export const del = <T>(url: string, params?: object, data?: object): Promise<T> => {
  return cachedRequest<T>({ method: 'delete', url, params, data })
}

function deleteNullParams(params: { [key: string]: unknown }) {
  for (const key in params) {
    if (params[key] === null || params[key] === undefined || params[key] === '') {
      delete params[key]
    }
  }
  return params
}

function getUniKey(config: AxiosRequestConfig): string {
  const { method, url, params, data } = config
  return `${method}-${url}-${JSON.stringify(params)}-${JSON.stringify(data)}`
}
相关推荐
pcplayer3 小时前
Delphi WebService 使用 HTTP Header 管理 Session
http·webservice·soap·web server·http header
小新讲网安5 小时前
HTTP请求走私攻击实战:CL.TE与TE.CL绕过前端服务器全解析
服务器·前端·网络·web安全·http·架构·漏洞
wear工程师1 天前
接口超时别只调大 timeout:Java HTTP 客户端其实有 3 段等待
java·http
蜡台1 天前
本机 局域网 搭建本地 HTTPS 域名完整方案
网络协议·http·https·openssl
ControlRookie1 天前
第13篇_Client 02|TCP 连接成功,为什么 HTTP 请求还没有成功
http·codesys·plc通信·controlrookie
IPdodo跨境网络2 天前
HTTP 与 SOCKS5 代理到底差在哪?用 Node.js 跑一次连接链路对比
http
小小龙学IT2 天前
gRPC 开源高性能 RPC 框架深度解析:从 HTTP/2 到跨语言微服务实战
http·rpc·开源
Misnearch3 天前
MCP以及底层协议、传输模式
http·mcp·json-rpc
Aision_4 天前
实习学习笔记:HTTP请求走私漏洞全方位解析
笔记·学习·安全·web安全·http·网络安全·网络攻击模型
zhao3266857514 天前
除了网页浏览,HTTP和HTTPS代理还能干啥?适用场景有哪些
网络协议·http·https