You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

5.9 KiB

前端统一错误响应格式适配

日期: 2026-01-27
类型: 功能增强
影响范围: API 客户端、类型定义

变更概述

适配后端统一错误响应格式(RFC-135),更新前端 API 客户端和类型定义,确保前后端响应格式一致。

问题背景

后端实现了统一响应格式:

{
  success: boolean;
  code: number;
  message: string;
  data: T | null;
  timestamp: string;
}

但前端类型定义和响应拦截器仍使用旧格式,缺少 successtimestamp 字段。

解决方案

1. 更新 API 响应类型定义

文件: client/src/types/api.ts

// 旧格式
export interface ApiResponse<T> {
  code: number;
  message: string;
  data: T;
}

// 新格式
export interface ApiResponse<T> {
  success: boolean;      // ✅ 新增
  code: number;
  message: string;
  data: T | null;        // ✅ 支持 null
  timestamp: string;     // ✅ 新增
}

2. 更新响应拦截器

文件: client/src/services/api/client.ts

成功响应处理

// 优先检查新格式(success 字段)
if (res && typeof res === 'object' && typeof res.success === 'boolean') {
  if (res.success) {
    return res.data;
  }
  
  // 业务错误
  const message = res.message || 'Unknown Error';
  const error = new Error(message);
  (error as any).code = res.code;
  (error as any).data = res.data;
  return Promise.reject(error);
}

// 兼容旧格式(code 字段)
if (res && typeof res === 'object' && typeof res.code === 'number') {
  if (res.code === 200) {
    return res.data;
  }
  // ...
}

错误响应处理

// 处理统一错误响应格式
const errorData = error.response?.data as any;
if (errorData && typeof errorData.success === 'boolean' && !errorData.success) {
  const message = errorData.message || 'Unknown Error';
  
  const customError = new Error(message);
  (customError as any).code = errorData.code;
  (customError as any).data = errorData.data;
  (customError as any).timestamp = errorData.timestamp;
  
  // 401/403 认证错误特殊处理
  if (errorData.code === 401 || errorData.code === 403) {
    useAppStore.getState().setUser(null);
    localStorage.removeItem('token');
    window.location.href = '/login';
  }
  
  return Promise.reject(customError);
}

兼容性

向后兼容

响应拦截器同时支持新旧两种格式:

  1. 新格式(优先):检查 success 字段
  2. 旧格式(兼容):检查 code === 200

这确保了在后端逐步迁移期间,前端不会出现问题。

错误对象增强

错误对象现在包含更多信息:

interface CustomError extends Error {
  code: number;        // HTTP 状态码
  data: any;           // 错误详情
  timestamp?: string;  // 错误时间戳
}

测试场景

1. 成功响应

// 后端返回
{
  "success": true,
  "code": 200,
  "message": "Success",
  "data": { "userId": "123" },
  "timestamp": "2026-01-27T08:00:00Z"
}

// 前端接收
{ "userId": "123" }  // 自动提取 data

2. 业务错误(400)

// 后端返回
{
  "success": false,
  "code": 400,
  "message": "验证码错误",
  "data": null,
  "timestamp": "2026-01-27T08:00:00Z"
}

// 前端捕获
catch (error) {
  console.error(error.message);  // "验证码错误"
  console.error(error.code);     // 400
}

3. 参数验证错误(422)

// 后端返回
{
  "success": false,
  "code": 422,
  "message": "body -> code: Field required",
  "data": { "errors": [...] },
  "timestamp": "2026-01-27T08:00:00Z"
}

// 前端捕获
catch (error) {
  console.error(error.message);  // "body -> code: Field required"
  console.error(error.data);     // { "errors": [...] }
}

4. 认证错误(401)

// 后端返回
{
  "success": false,
  "code": 401,
  "message": "认证失败",
  "data": null,
  "timestamp": "2026-01-27T08:00:00Z"
}

// 前端行为
// 1. 清除用户状态
// 2. 删除 token
// 3. 跳转登录页

使用示例

API 调用

import apiClient from '@/services/api/client';

// 成功场景
try {
  const user = await apiClient.get('/api/v1/users/me');
  console.log(user);  // 直接获取 data
} catch (error) {
  console.error(error.message);  // 统一错误消息
  console.error(error.code);     // HTTP 状态码
}

// 错误场景
try {
  await apiClient.post('/api/v1/auth/login/phone', {
    phone: '18046315592',
    code: '000000'
  });
} catch (error) {
  // error.message: "验证码错误"
  // error.code: 400
  showToast(error.message);
}

类型安全

import type { ApiResponse } from '@/types/api';

// 完整响应类型
const response: ApiResponse<User> = {
  success: true,
  code: 200,
  message: 'Success',
  data: { userId: '123', username: 'test' },
  timestamp: '2026-01-27T08:00:00Z'
};

// data 可以为 null
const errorResponse: ApiResponse<null> = {
  success: false,
  code: 400,
  message: 'Error',
  data: null,
  timestamp: '2026-01-27T08:00:00Z'
};

影响范围

受影响文件

  • client/src/types/api.ts - 类型定义
  • client/src/services/api/client.ts - 响应拦截器

不受影响

  • 所有使用 apiClient 的业务代码无需修改
  • 响应拦截器自动处理格式转换
  • 错误处理逻辑保持不变

相关文档

部署说明

无需特殊部署步骤,前端构建后即可生效。

后续优化

  1. 添加响应时间监控(利用 timestamp 字段)
  2. 实现错误重试机制(基于 code 判断)
  3. 统一错误提示组件(显示 message
  4. 添加错误追踪(记录 timestampcode