3.3 错误处理:契约的违约条款


3.3 错误处理:契约的违约条款

本节摘要:错误处理是把"违约时刻"做成机器可读的违约书——状态码表层级、错误码表业务细分、响应体结构统一承载。本节用一张状态码族谱整理 4xx/5xx 的分工,再用一个"字段违规清单"案例演示错误体怎么让客户端能自动识别并给出友好提示。

一次把错误全部写成 400 的返修

接手过一个第三方接口:整张错误处理表里只有一条 400,无论参数错、凭证过期、还是资源不存在,响应体清一色写"请求失败"。客户端想给用户提示,得去解析一段中文人话;想判断能不能重试,根本无从下手。后来一次对方把状态码升级,所有客户端一起懵——既不知道哪里错,也不知道该不该重发。错误处理如果只做到"返回失败",离"契约级的违约条款"还差得远。本节就从"机器可读"这四个字讲起。

学习目标

阅读完本节,你应当能够:

  1. 区分 4xx(客户端)与 5xx(服务器)错误及常用状态码的分工。
  2. 设计"应用错误码 + 可读消息 + 详情数组"三层的错误体结构。
  3. 让错误体既给人看又给机器判(稳定、一致)。
  4. 用统一错误结构快速定位一次故障属于请求还是服务端。

错误处理要的是机器可读,不是打印一行话

很多接口把错误处理当成"把错误消息打印进响应体"。这离契约思维还差一截。错误的本质是"违约时刻",要达成的目标是机器可读:客户端收到一个错误,不用解析人话、不用猜,就能知道"这是谁的错、是什么类型的错、该不该重试、怎么修"。这要求错误具备三个层次:状态码定层级、错误码定业务、结构定表达。

把三个层次各管什么先钉死:状态码回答"是谁的问题、归哪个大类",往宽了分;错误码回答"具体是哪一档子业务问题",往细了分;错误体结构回答"怎么把细节整齐地交给客户端",往统一分。三层缺一层,错误处理都会在某次故障里露怯——要么客户端分不清该不该重试,要么解析不出具体字段错在哪。

二、状态码:层级的违约书

先用状态码划总纲(一张族谱图可一眼记住):

二、状态码:层级的违约书

图:HTTP 状态码族谱

关键分层:4xx 表示"你这请求本身有问题,改它才对",5xx 表示"服务器没搞定,重试它可能有用"。这个分界直接指导客户端的作文——4xx 不盲目重试(重试更错),5xx 可以带退避重试。

常用的几个别只认数字,要对号入座:

状态码 含义 该不该重试
400 请求格式/参数错 不该,改了再发
401 凭证缺失或过期 先重新认证
403 无权限,凭证有效但不许做 不该
404 资源不存在 先查 URI 是否写对
409 与当前状态冲突 看清冲突再决定
429 触达限流 按 Retry-After 退避
500 服务器内部错 可带退避试
503 服务暂时不可用 按 Retry-After 退避

这一张表足够日常用;小到别把"凭证过期"写成 400,大到别把"服务过载"写成 500 抹掉重试跳板——状态码选得准,客户端重试策略才写得对。

三、错误体:三层的违约书正文

状态码只告诉"谁的错",还不够告诉"具体哪里错、怎么修"。要用统一错误体承载细节。一个被普遍采用的三层结构:

字段 作用
应用错误码 code 机器可判定的稳定业务码
可读消息 message 人类能懂,可展示给用户
详情数组 details 逐字段列违规,便于定位
{ "code": "INVALID_INPUT", "message": "提交的内容不合法。", "details": [ {"field": "email", "reason": "邮箱格式不正确"}, {"field": "password", "reason": "密码至少 8 位"} ] }

拿到这份错误体,客户端能:

  • code 做分支判断(比如 INVALID_INPUT 就高亮表单对应字段);
  • details 里的 field 勾字段、贴 reason 展示给用户;
  • message 兜底给人做通用提示。

关键是结构全接口一致,字段名别一会 message 一会 msg。机器才能稳定解析。

code 不是让你随手发明的临时码,它本身值得一张"错误码目录"。把每个 code 配上一句机器可依赖的稳定含义、一个公开的文档入口,日子久了就能沉淀成一份"错误码字典",客户端拿着它做分支、告警、降级。目录的管理有两个纪律:一是新码要登记、旧码要标记弃用,别让 INVALID_INPUTINVALID_INPUT_V2 并存叫人懵;二是不要把"这次的具体参数值"编进 code(如 EMAIL_ALREADY_TAKEN_a@b.com),具体值放 detailsmessagecode 只做稳定归类。码越稳定,客户端越敢长期依赖,错误契约才真正"可 trust"。

四、一次故障定位演练

把三层结构用到实战定位。你收到一条用户反馈:"登录一直报错"。回答里带上模拟日志:

HTTP/1.1 401 Unauthorized {"code":"AUTH_FAILED","message":"凭证无效。","details":[]}

背景:用户发登录请求。
操作:服务器验凭证未通过。
结果:401 + AUTH_FAILED
解读:401 属 4xx,问题在客户端(凭证错/过期),不是服务器,重试无用,应引导重新输入或刷新令牌。
变式:若同一请求收到 503 + SERVICE_OVERLOADEDRetry-After 头,则是 5xx,且服务器明说"稍后再来",客户端应带退避重试而非报硬错误。

⚠️ 常见坑:把所有错误都返回 400,错误详情里才写"是 401 还是 409"。状态码分层的意义就在"不用解析 body 也能判断大方向",把层次塞进 body 等于自废武功。

💡 关键直觉:错误契约的价值在"一致性 + 机器可读"。宁可错误码少而稳定,也不要用一排会变的临时码。稳定的 code 是客户端能长期 trust 的锚。

本节要点回顾

  • 要点一:状态码分层:4xx 客户端要改,5xx 服务器要修,指导重试策略。
  • 要点二:错误体三层结构:应用错误码、可读消息、详情数组。
  • 要点三:错误结构全接口一致,字段名稳定机器才可判。
  • 要点四:401/409/404 各代表一类具体情形,别挤成一个 400。
  • 要点五:5xx 带退避重试,4xx 不盲目重试。
  • 要点六:稳定、一致的错误契约,是高质量接口的底线之一。

错误条款兜住了"违约时刻",下一节给契约上第一道防线——认证、授权、传输加密三件套。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U