封装 request 附带 xss 过滤

TypeScript   2025-06-01 11:07   33   0  
import axios from 'axios'
import type { AxiosInstance, InternalAxiosRequestConfig, AxiosResponse } from 'axios'
import Cookies from 'js-cookie'
import { useMessage } from '@/hooks/useMessage'

const message = useMessage()

// 创建 axios 实例
const service: AxiosInstance = axios.create({
  // API 的 base_url
  baseURL: import.meta.env.VITE_API_BASE_URL || '/api',
  // 请求超时时间
  timeout: Number(import.meta.env.VITE_APP_API_TIMEOUT) || 15000,
  headers: {
    'Content-Type': 'application/json;charset=utf-8'
  }
})

// 请求队列管理
const pendingRequests = new Map()
const cancelToken = axios.CancelToken.source()

// 超时配置
const TIMEOUT_CONFIG = {
  default: 15000,
  upload: 30000,
  download: 60000
}

// 生成请求的唯一键
const generateRequestKey = (config: InternalAxiosRequestConfig) => {
  const { url, method, params, data } = config
  return [url, method, JSON.stringify(params), JSON.stringify(data)].join('&')
}

// 添加请求到队列
const addPendingRequest = (config: InternalAxiosRequestConfig) => {
  const requestKey = generateRequestKey(config)
  if (!pendingRequests.has(requestKey)) {
    config.cancelToken = cancelToken.token
    pendingRequests.set(requestKey, config)
  }
}

// 从队列中移除请求
const removePendingRequest = (config: InternalAxiosRequestConfig) => {
  const requestKey = generateRequestKey(config)
  pendingRequests.delete(requestKey)
}

// 清空所有请求
const clearPendingRequests = () => {
  pendingRequests.forEach((config) => {
    if (config.cancelToken) {
      config.cancelToken.cancel('请求被取消')
    }
  })
  pendingRequests.clear()
}

// XSS 过滤
const xssFilter = (data: any): any => {
  if (typeof data === 'string') {
    return data.replace(/[<>]/g, '')
  }
  if (Array.isArray(data)) {
    return data.map(item => xssFilter(item))
  }
  if (typeof data === 'object' && data !== null) {
    const result: any = {}
    for (const key in data) {
      result[key] = xssFilter(data[key])
    }
    return result
  }
  return data
}

// 请求拦截器
service.interceptors.request.use(
  (config: InternalAxiosRequestConfig) => {
    // 从 Cookie 中获取 token
    const token = Cookies.get('access_token')
    if (token) {
      config.headers.Authorization = `${token}`
    }

    // 添加 CSRF Token
    const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content')
    if (csrfToken) {
      config.headers['X-CSRF-Token'] = csrfToken
    }

    // 添加请求到队列
    addPendingRequest(config)

    // 设置超时时间
    if (config.url?.includes('upload')) {
      config.timeout = TIMEOUT_CONFIG.upload
    } else if (config.url?.includes('download')) {
      config.timeout = TIMEOUT_CONFIG.download
    } else {
      config.timeout = TIMEOUT_CONFIG.default
    }

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

// 响应拦截器
service.interceptors.response.use(
  (response: AxiosResponse) => {
    // 从队列中移除请求
    removePendingRequest(response.config)

    const res = response.data
    // 这里可以根据后端返回的状态码做相应的处理
    if (res.code === 200 || res.code === 0) {
      // XSS 过滤
      return xssFilter(res)
    } else {
      message.error(res.message || '请求失败')
      return Promise.reject(new Error(res.message || '请求失败'))
    }
  },
  (error) => {
    // 从队列中移除请求
    if (error.config) {
      removePendingRequest(error.config)
    }
    message.error(error.message || '网络错误')
    return Promise.reject(error)
  }
)

// 封装 GET 请求
export function get<T>(url: string, params?: any): Promise<T> {
  return service.get(url, { params })
}

// 封装 POST 请求
export function post<T>(url: string, data?: any): Promise<T> {
  return service.post(url, data)
}

// 封装 PUT 请求
export function put<T>(url: string, data?: any): Promise<T> {
  return service.put(url, data)
}

// 封装 DELETE 请求
export function del<T>(url: string): Promise<T> {
  return service.delete(url)
}


export default service

调用:

import { get, post, put, del } from '@/utils/request'
interface ApiResponse<T> {
  code: number
  data: T
  result: T
  message: string
}
export const systemApi = {
   // 获取服务器时间
   getServerTime: () => {
    return get<ApiResponse<number>>('自定义接口')
  }
}
try {
  const res = await systemApi.getServerTime()
  // 将时间戳转换为 ISO 字符串
  serverTime.value = new Date(res.result).toISOString()
} catch (error) {
  console.error('获取服务器时间失败:', error)
}


下一篇
没有了