前端接口契约治理实践:MSW + 错误码 + OpenAPI 的一次落地

背景

在一个中后台项目中,我们遇到了一个典型但长期被低估的问题:

  • Swagger 文档先于页面设计完成
  • 页面实际需要的数据结构与现有接口不一致
  • 错误码基本等同于 HTTP Status,语义不足
  • 前端页面中大量 if (res.code !== 0) + toast(res.message)

结果是:

  • 前后端对接口结构理解不一致
  • Mock 与真实接口行为不一致
  • 错误提示不可控,难以国际化
  • 后续维护成本持续升高

我们最终决定 重构接口契约的生产流程,而不是继续修补页面代码。


目标

这次调整的目标非常明确:

  1. 前端可以先定义接口形态
  2. 后端可以明确对齐的契约
  3. 错误码具备业务语义,而不是 HTTP 映射
  4. 页面层不再关心 codemessage
  5. Mock / 测试 / 生产行为保持一致

核心决策

1. 使用 MSW 作为接口契约的“前置实现”

我们没有继续使用“纯 mock 数据”的方式,而是把 MSW 当作接口协议的可执行定义

  • 明确 URL
  • 明确 request / response 结构
  • 明确错误码返回

MSW 的职责不是“造假数据”,而是:

在后端未完成前,冻结接口语义


2. 新建独立契约仓库

我们将以下内容从前端项目中剥离:

  • 错误码表
  • 通用响应结构
  • OpenAPI 文档

形成一个独立仓库,例如:

1
2
3
4
5
6
7
8
9
10
api-contract/
├── error-codes/
│ └── common.yaml
├── schemas/
│ ├── ApiResponse.yaml
│ └── Pagination.yaml
├── apis/
│ ├── auth.yaml
│ └── user.yaml
└── openapi.yaml

这个仓库的唯一职责

描述系统“允许发生什么”,而不是“如何实现”。

3. 错误码不再等价于 HTTP 状态码

我们重新设计了业务错误码段:

  • 0:成功
  • 1xxx:通用错误(参数、鉴权、限流)
  • 4xxx:业务级客户端错误
  • 5xxx:服务端错误

例如:

1
2
3
4
5
6
7
8
9
1002:
name: unauthorized
http_status: 401
message: 未认证或认证失效

4001:
name: resource_not_found
http_status: 404
message: 资源不存在

HTTP Status 只用于传输层语义,业务语义只由错误码表达


4. 统一 ApiResponse 结构

最终我们选择了包装式返回结构

1
2
3
4
code: number
message: string
data: any
trace_id: string

分页数据被视为业务数据的一部分,而不是协议的一部分:

1
2
3
4
5
data:
list: T[]
total: number
page: number
pageSize: number

这样可以避免:

  • 多种返回结构并存
  • request 层复杂判断
  • 泛型难以复用

前端错误处理的收口

request 层是唯一的错误判断入口

1
2
3
4
5
if (res.code !== 0) {
handleApiError(res)
return Promise.reject(res)
}
return res.data

页面层不再判断 code

页面统一使用:

1
2
3
4
5
6
try {
await apiCreateUser()
toast.success('操作成功')
} catch {
// 不处理错误展示
}

页面只做三件事:

  1. 调用接口
  2. 成功处理
  3. 必要时对特定错误码做行为响应(跳转 / 弹窗)

错误提示与 i18n 的关系

我们将错误文案分为两层:

通用错误(必须 i18n)

  • unauthorized
  • forbidden
  • invalid_param
  • internal_error

这些错误在多个页面、多个接口都会出现。

业务错误(可选 i18n)

  • key_limit_reached
  • order_already_paid

业务错误优先使用 i18n,如果未配置则使用后端 message 兜底。


MSW 与 OpenAPI 的关系

在这套体系中:

  • MSW 是可执行契约
  • OpenAPI 是文档化契约
  • 后端实现以 OpenAPI 为准

实际流程是:

  1. 前端用 MSW 定义接口形态
  2. 将 MSW 对应结构登记到 OpenAPI
  3. 后端根据 OpenAPI 实现
  4. 后端生成 Swagger
  5. 前端可用 Orval 生成类型与请求代码

Orval 生成的 MSW handler 主要用于:

  • 自动补齐接口
  • 校验契约是否被破坏
  • 辅助测试,而不是主 Mock 来源

最终效果

这次调整带来的变化非常直接:

  • 页面代码明显变薄
  • 错误提示完全一致
  • Mock / 真接口无行为差异
  • 新接口设计成本下降
  • 前后端沟通以“契约 diff”为主

总结

这次实践的核心并不是 MSW、OpenAPI 或 Orval 本身,而是一个观念转变:

接口不是后端“提供”的,而是系统“约定”的

当契约先于实现存在,很多长期存在的协作问题会自然消失。

前端接口契约治理实践:MSW + 错误码 + OpenAPI 的一次落地

https://www.f2iclo.cn/2026/02/10/howtousemsw02/

作者

小郑

发布于

2026-02-10

更新于

2026-02-10

许可协议

评论