/** * HTTP 错误处理模块 * * 提供统一的 HTTP 请求错误处理机制 * * ## 主要功能 * * - 自定义 HttpError 错误类,封装错误信息、状态码、时间戳等 * - 错误拦截和转换,将 Axios 错误转换为标准的 HttpError * - 错误消息国际化处理,根据状态码返回对应的多语言错误提示 * - 错误日志记录,便于问题追踪和调试 * - 错误和成功消息的统一展示 * - 类型守卫函数,用于判断错误类型 * * ## 使用场景 * * - HTTP 请求拦截器中统一处理错误 * - 业务代码中捕获和处理特定错误 * - 错误日志收集和上报 * * @module utils/http/error * @author Art Design Pro Team */ import { AxiosError } from 'axios' import { ApiStatus } from './status' import { $t } from '@/locales' // 错误响应接口 export interface ErrorResponse { /** 错误状态码 */ code: number /** 错误消息 */ msg: string /** 错误附加数据 */ data?: unknown } // 错误日志数据接口 export interface ErrorLogData { /** 错误状态码 */ code: number /** 错误消息 */ message: string /** 错误附加数据 */ data?: unknown /** 错误发生时间戳 */ timestamp: string /** 请求 URL */ url?: string /** 请求方法 */ method?: string /** 错误堆栈信息 */ stack?: string } // 自定义 HttpError 类 export class HttpError extends Error { public readonly code: number public readonly data?: unknown public readonly timestamp: string public readonly url?: string public readonly method?: string constructor( message: string, code: number, options?: { data?: unknown url?: string method?: string } ) { super(message) this.name = 'HttpError' this.code = code this.data = options?.data this.timestamp = new Date().toISOString() this.url = options?.url this.method = options?.method } public toLogData(): ErrorLogData { return { code: this.code, message: this.message, data: this.data, timestamp: this.timestamp, url: this.url, method: this.method, stack: this.stack } } } /** * 获取错误消息 * @param status 错误状态码 * @returns 错误消息 */ const getErrorMessage = (status: number): string => { const errorMap: Record = { [ApiStatus.unauthorized]: 'httpMsg.unauthorized', [ApiStatus.forbidden]: 'httpMsg.forbidden', [ApiStatus.notFound]: 'httpMsg.notFound', [ApiStatus.methodNotAllowed]: 'httpMsg.methodNotAllowed', [ApiStatus.requestTimeout]: 'httpMsg.requestTimeout', [ApiStatus.internalServerError]: 'httpMsg.internalServerError', [ApiStatus.badGateway]: 'httpMsg.badGateway', [ApiStatus.serviceUnavailable]: 'httpMsg.serviceUnavailable', [ApiStatus.gatewayTimeout]: 'httpMsg.gatewayTimeout' } return $t(errorMap[status] || 'httpMsg.internalServerError') } /** * 处理错误 * @param error 错误对象 * @returns 错误对象 */ export function handleError(error: AxiosError): never { // 处理取消的请求 if (error.code === 'ERR_CANCELED') { console.warn('Request cancelled:', error.message) throw new HttpError($t('httpMsg.requestCancelled'), ApiStatus.error) } const statusCode = error.response?.status const errorData = error.response?.data as any const errorMessage = errorData?.msg || errorData?.message || error.message const requestConfig = error.config // 无 HTTP 响应:断网、后端未启动、连接被拒绝、超时、浏览器休眠后首包失败等 if (!error.response) { const axiosCode = error.code if (axiosCode === 'ECONNABORTED') { throw new HttpError($t('httpMsg.requestTimeout'), ApiStatus.requestTimeout, { url: requestConfig?.url, method: requestConfig?.method?.toUpperCase() }) } throw new HttpError($t('httpMsg.networkError'), ApiStatus.networkError, { url: requestConfig?.url, method: requestConfig?.method?.toUpperCase() }) } // 处理 HTTP 状态码错误 const message = statusCode ? getErrorMessage(statusCode) : errorMessage || $t('httpMsg.requestFailed') throw new HttpError(message, statusCode || ApiStatus.error, { data: error.response.data, url: requestConfig?.url, method: requestConfig?.method?.toUpperCase() }) } /** * 显示错误消息 * @param error 错误对象 * @param showMessage 是否显示错误消息 */ export function showError(error: HttpError, showMessage: boolean = true): void { if (showMessage) { ElMessage.error(error.message) } // 记录错误日志 console.error('[HTTP Error]', error.toLogData()) } /** * 显示成功消息 * @param message 成功消息 * @param showMessage 是否显示消息 */ export function showSuccess(message: string, showMessage: boolean = true): void { if (showMessage) { ElMessage.success(message) } } /** * 判断是否为 HttpError 类型 * @param error 错误对象 * @returns 是否为 HttpError 类型 */ export const isHttpError = (error: unknown): error is HttpError => { return error instanceof HttpError }