设想你走进一家生意火爆的餐厅,菜单上每道菜都有唯一编号:“凉菜-A12”“热菜-B08”“汤品-C03”。当你想点一道“烤鸭”却被告知“烤鸭已售罄”时,服务员会根据退菜流程告诉你:菜名(哪个菜)、原因(为什么退)、建议(换什么菜)、经手人(谁处理的)。这套“编号 + 分类 + 告知”的系统,正是API设计中错误码与错误响应的灵魂——调用方收到一个错误时,能不能立刻知道“哪里错了、为什么错、该怎么办”。 本文用餐厅菜单的视角,带你从零看懂API错误码体系的设计方法、错误分类与HTTP状态码的映射关系、标准响应格式,以及错误码如何优雅地“改版”而不让老顾客迷路。你会发现,一套好的错误处理设计,就像一份好菜单一样让人安心。

岗位关联

学完这一节,你将具备为AI服务API设计“菜单级”错误码体系的能力,让调用方能像看菜单一样快速定位并解决错误,这是每个AI工程团队的必修基本功。

目录
  1. 可视化演示
  2. 错误码设计原则
  3. 错误码分类体系
  4. 状态码映射规则
  5. 错误响应格式
  6. 统一错误处理
  7. 错误码演进策略
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示错误码分类体系、错误响应处理流程与重试退避策略,理解API错误处理的设计原则与工程实践。

本节配有交互动效(进入视口后自动加载,约 3KB)。

错误码分类体系 4xx客户端错误(请求参数错误/认证失败/资源不存在) 5xx服务端错误(内部异常/超时/服务不可用)+ AI特有错误 错误 = f(类型,可重试?, 严重度) 分层分类体系

错误码设计原则

餐厅菜单的编号绝不是随手写的——如果“凉菜-A12”和“热菜-A12”同时存在,服务员就得靠吼才能分清。API的错误码也一样,它承担着“服务员和顾客之间无歧义沟通”的重任。设计错误码的第一课,就是学会像资深菜单设计师一样,给每个错误一个唯一的、好认的、能成长的编号。

所谓唯一性,是指一个错误码只对应一种错误,绝不同码异义;可读性,是让开发者看到“10402“时能直觉感到”这跟参数有关“;可扩展性,则是为一两年后的新错误预留编号区间,不必推翻重来。这三条原则,决定了你家的”菜单编号”是让调用方省心还是抓狂。

为了支撑这三个原则,业界普遍采用分段式编码。一个五位错误码被拆成模块、类别、序号三段,就像菜名被拆成“菜系字母 + 类型数字 + 流水号”:

想象一下,当后厨(服务端)高喊“热菜-B08出问题了”,服务员(API网关)立刻知道是哪道菜,顾客(调用方)也能马上在菜单上找到对应条目。这种“三段式编号”的魔力,正在于把混乱的错误世界变成了可检索的菜单。

Cerr=Pmod⋅104+Tcls⋅102+Sseq\displaystyle C_{\text{err}} = P_{\text{mod}} \cdot 10^4 + T_{\text{cls}} \cdot 10^2 + S_{\text{seq}}
错误码由三部分组成:模块前缀决定归属于哪个子系统,类别决定错误大类,序号定位到具体错误点。
  1. 模块前缀 P代表错误来源的子系统编号,类似菜单头字母“凉菜区=A、热菜区=B”,决定了错误码的第一位数字。
    Pmod∈{1,2,3,4,5}\displaystyle P_{\text{mod}} \in \{1, 2, 3, 4, 5\}
  2. 类别位 T同一模块内再区分错误大类,如参数缺失、参数无效,占据中间两位。
    Tcls⋅102\displaystyle T_{\text{cls}} \cdot 10^2
  3. 序号位 S同一类别下的具体错误流水号,从 00 到 99 提供一百个位置,保证唯一。
    Sseq\displaystyle S_{\text{seq}}

错误码分类体系

好的餐厅会把菜单分成前菜、汤品、主菜、甜品几个大区,顾客饿不饿都能快速定位。错误码世界同样需要这样的“菜单分区”——按照错误性质,把千奇百怪的问题归入几个互不重叠的大类,调用方一看首位数字就知道该找谁、该怎么办。

在AI服务API中,最常见的分类是按“谁的问题”划分:参数类错误(首码1)是调用方传参不规范;鉴权类错误(首码2)是身份凭证缺失或权限不够;资源类错误(首码3)是请求的目标不存在;限流类错误(首码4)是请求过于频繁;服务类错误(首码5)是服务端自身状态异常。这五大类用首位数字区分,形成清晰的“菜单分区”:

有了这套分类体系,AI服务API的调用方在凌晨三点收到“20403“时,不用看文档也能断定——这是鉴权类问题,多半是Token过期了,先去刷新凭证再说。分类的价值,就是把”排查错误“变成了”按区翻菜单”。

首码=⌊Cerr104⌋={1,参数类2,鉴权类3,资源类4,限流类5,服务类\displaystyle \text{首码} = \left\lfloor \frac{C_{\text{err}}}{10^4} \right\rfloor = \begin{cases} 1, & \text{参数类} \\ 2, & \text{鉴权类} \\ 3, & \text{资源类} \\ 4, & \text{限流类} \\ 5, & \text{服务类} \end{cases}
错误码首位数字 = 错误大类,五类错误互不重叠,定位问题像翻菜单一样快。
  1. 取首码用整除取整拿到错误码的万位数字,等价于“看菜单分区”。
    ⌊Cerr104⌋\displaystyle \left\lfloor \frac{C_{\text{err}}}{10^4} \right\rfloor
  2. 映射到类1 是参数、2 是鉴权、3 是资源、4 是限流、5 是服务,首位数字就是分区的标签。
    {1,2,3,4,5}→{参数,鉴权,资源,限流,服务}\displaystyle \{1,2,3,4,5\} \to \{\text{参数},\text{鉴权},\text{资源},\text{限流},\text{服务}\}

状态码映射规则

服务员把后厨的“烤鸭没了”翻译成顾客能懂的话,叫“推销新菜”;API网关把内部错误码翻译成调用方能懂的HTTP状态码,叫“语义映射”。HTTP状态码是互联网世界的通用语言,4xx表示“顾客,是你这道菜点错了”,5xx表示“先生,是厨房灶台坏了,请稍等”。翻译得准不准,直接决定了调用方能不能做出正确反应。

映射的核心规则是“按语义匹配,不硬凑数字”。参数类错误映射到 400 Bad Request;鉴权失败映射到 401 Unauthorized;权限不足映射到 403 Forbidden;资源不存在映射到 404 Not Found;限流触发映射到 429 Too Many Requests;服务端内部故障统一映射到 500。这条规则的数学本质是一张从错误码到状态码的映射表:

一个好的服务员不会把“菜卖完了”说成“厨房爆炸了”,好的API网关也不会把参数错误报成500。映射规则的意义,就是让HTTP状态码成为错误码的“大众翻译”,任何语言、任何框架的开发者都能秒懂。

f(Cerr)={4xx,首码∈{1,2,3,4}5xx,首码=5\displaystyle f(C_{\text{err}}) = \begin{cases} 4\text{xx}, & \text{首码} \in \{1, 2, 3, 4\} \\ 5\text{xx}, & \text{首码} = 5 \end{cases}
客户端类错误(首码1-4)映射到 4xx,服务端类错误(首码5)映射到 5xx,让调用方第一时间判断该改自己还是等对方。
  1. 客户端错误映射当首码属于参数、鉴权、资源、限流时,映射到 4xx——告诉调用方“请修改你的请求”。
    f(Cerr)=4xx,首码∈{1,2,3,4}\displaystyle f(C_{\text{err}}) = 4\text{xx},\quad \text{首码} \in \{1,2,3,4\}
  2. 服务端错误映射当首码为 5 时,映射到 5xx——告诉调用方“问题在我们,请稍后重试或联系我们”。
    f(Cerr)=5xx,首码=5\displaystyle f(C_{\text{err}}) = 5\text{xx},\quad \text{首码} = 5
  3. 具体对应1→400,2→401/403,3→404,4→429,5→500,形成精确到个位的映射表。
    f(10402)=400;f(50500)=500\displaystyle f(10402) = 400;\quad f(50500) = 500

错误响应格式

当餐厅决定退掉一道菜时,服务员会填一张标准退菜单:菜名、退菜原因、替换建议、经手人编号、退菜时间。没有这张统一格式的单子,后厨和账台就得来回扯皮。API的错误响应也一样,没有一个标准模板,每个接口都自创格式,调用方就得为每个接口写一套解析逻辑,那是灾难。

标准的错误响应应当是一份“五字段退菜单”:code 是机器可读的错误码(顾客拿它查菜单);message 是让开发者一眼看懂的人类描述(退菜原因);details 是可选的补充详情(哪几个参数不对);traceId 是贯穿整条链路的追踪标识(经手人编号,排查日志全靠它);timestamp 是错误发生时刻(留痕备查)。这五个字段构成一个稳定元组:

试想,调用方收到一份只有“发生错误”四个字的响应,跟收到一份“code=10402, message=参数order_id缺失,traceId=8f3a……”的响应,排查成本天差地别。统一响应格式,就是把“退菜”这件事从混乱争吵变成按单办事的标准化流程。

Rerror=⟨code, message, details, traceId, timestamp⟩\displaystyle R_{\text{error}} = \langle \text{code},\ \text{message},\ \text{details},\ \text{traceId},\ \text{timestamp} \rangle
错误响应 = 错误码 + 人类描述 + 补充详情 + 追踪标识 + 时间戳,五要素齐备,调用方才能“拿着单子找回对应环节”。
  1. code 错误码机器可读的唯一编号,程序用它做分支判断,等价于退菜单上的“菜名编号”。
    code∈N\displaystyle \text{code} \in \mathbb{N}
  2. message 描述人类可读的简要说明,方便开发者快速定位问题原因。
    message:Error→String\displaystyle \text{message}: \text{Error} \to \text{String}
  3. traceId 追踪标识一次请求全链路的唯一ID,日志系统靠它串起所有环节,等价于“经手人编号”。
    traceId=Hash(请求上下文)\displaystyle \text{traceId} = \text{Hash}(\text{请求上下文})

统一错误处理

餐厅里每天都可能发生纠纷——菜咸了、上慢了、算错账了。如果每桌客人都找后厨吵架,后厨就没法做菜了。聪明的餐厅会设一位值班经理,所有投诉先到他这里,由他按统一流程分诊处理。API世界里的“值班经理”,就是统一错误处理机制。

它的工作流程非常像值班经理接单:先全局拦截所有异常(不论什么错误都不直接裸奔返回);再记录日志留痕(像经理在小本本上记一笔);接着分类转换——把底层异常归类成标准错误码,并做脱敏处理(绝不让内部堆栈裸露给调用方);最后输出标准格式的错误响应。这整个流程可以用一个复合函数表达:

这套机制的妙处在于:业务代码只需专注抛异常,其余全部交给“值班经理”。无论是数据库连不上、还是第三方AI模型超时,调用方拿到的永远是同一套格式的“退菜单”——清晰、安全、可追溯。

Rfinal=ϕ(Eraw),ϕ=sanitize∘classify∘log\displaystyle R_{\text{final}} = \phi\left( E_{\text{raw}} \right), \quad \phi = \text{sanitize} \circ \text{classify} \circ \text{log}
原始异常经过“记录 → 分类 → 脱敏”三步复合处理,最终输出标准错误响应,全程不需业务代码干预。
  1. 记录 log第一时间把原始异常写入日志系统,保留现场证据,等价于经理先记小本本。
    log(Eraw)→L\displaystyle \text{log}(E_{\text{raw}}) \to \mathcal{L}
  2. 分类 classify把底层异常归类到五大错误类别,映射出标准错误码。
    classify:E→Capi\displaystyle \text{classify}: \mathcal{E} \to \mathcal{C}_{api}
  3. 脱敏 sanitize过滤掉堆栈、SQL、内部路径等敏感信息,再包装成标准响应结构。
    sanitize:Capi→Rerror\displaystyle \text{sanitize}: \mathcal{C}_{api} \to R_{\text{error}}

错误码演进策略

菜单每个季度都会改版,但聪明的餐厅绝不会把招牌菜从“B08“改成”C15“——老顾客会找不到,账台也会乱套。API错误码的演进同样讲究”旧客不流失“:已经发布的错误码,含义绝不动摇;新增错误码只占用全新的编号;废弃的错误码也要在文档中保留位置,标注”已弃用”。

这就是向后兼容策略:版本升级只做加法、不做减法。v2 版本的映射函数必须保留 v1 版本所有稳定错误码的映射关系,新错误码开辟新的编号区间。用数学语言表达就是:

老顾客按老号码点菜不会出错,新顾客用新号码也能找到新菜品——这就是错误码演进的最佳状态。升级API时,调用方无需修改任何一行判断逻辑,因为你的“菜单”虽然加厚了,但每一道老菜的位置,都还在老地方。

fv2(c)=fv1(c),∀ c∈Cstable\displaystyle f_{\text{v2}}(c) = f_{\text{v1}}(c), \quad \forall\, c \in \mathcal{C}_{\text{stable}}
所有已承诺的错误码,在新版本中的 HTTP 映射和含义必须保持不变;只有从未发布过的编号区间才允许重新规划。
  1. 稳定集合不是所有错误码都无条件保留,只有对外承诺过的稳定码才受到保护。
    Cstable⊆Capi\displaystyle \mathcal{C}_{\text{stable}} \subseteq \mathcal{C}_{\text{api}}
  2. 映射不变新版本函数在稳定集合上完全等于旧版本函数,保证老调用方无感升级。
    fv2(c)=fv1(c),∀c∈Cstable\displaystyle f_{\text{v2}}(c) = f_{\text{v1}}(c),\quad \forall c \in \mathcal{C}_{\text{stable}}
  3. 新区间新错误码只在原分类下的空余序号中产生,同时更新文档目录。
    新码∈Capi∖Cv1\displaystyle \text{新码} \in \mathcal{C}_{\text{api}} \setminus \mathcal{C}_{\text{v1}}

AI 应用场景

AI大模型服务的错误码体系设计

阶段:AI服务API设计与部署

解决的问题:大模型API的调用方经常面对“超时、内容审核拦截、上下文超长、限流”等AI特有错误。传统HTTP状态码只能表达“服务端错误”,无法告诉调用方是“Token超长”还是“触发敏感词审核”,导致客户端无法针对性地截断请求或调整Prompt。引入五类错误码体系后,调用方可以按code精准分流处理。

具体案例:某AI写作平台对外提供文本生成API,调用方传入超长文本时收到 10403(参数类·上下文超长)错误码与 400 状态码。客户端SDK捕获该码后自动截断文本并重试,用户无感知。若收到 20402(鉴权类·余额不足),SDK则跳转充值页。通过错误码分类,AI服务的异常处理从“黑盒”变成“可编程”。

电商下单链路的统一错误处理

阶段:微服务网关与统一响应层

解决的问题:分布式系统中,订单服务、库存服务、支付服务各自返回不同格式的错误,前端联动页面难以统一处理。引入统一错误处理机制后,网关像“值班经理”一样拦截所有异常,转换为标准五字段响应,并自动附带traceId串联全链路日志。

具体案例:用户在秒杀场景下提交订单,库存服务抛出的“库存不足”被网关捕获,转换为 30409(资源类·库存冲突)+ 409 状态码。前端收到后弹出“手慢了,商品已抢光”,同时traceId被记录到日志系统,运维可在十秒内定位是哪次扣减操作失败。

第三方API对接的开发者体验优化

阶段:API文档与SDK设计

解决的问题:AI能力(如语音识别、图像生成)被第三方开发者集成时,错误码混乱会直接导致集成成本飙升。清晰的错误码体系和文档同步策略,能让开发者“见码知错”,减少工单咨询量。

具体案例:某视觉识别API的v2版本发布时,严格遵守向后兼容策略:旧码 30404 继续表示“图片不存在”,新增 10406 表示“图片分辨率过低”并同步更新在线文档。已有调用方零改动升级,新开发者通过文档中错误码检索页快速定位问题,技术支持工单量下降40%。

Python 演示

python78 行
import jsonimport timeimport uuid  class ErrorCode:    """错误码枚举:模块前缀(1) + 类别(2位) + 序号(2位)"""    PARAM_MISSING = 10400    PARAM_INVALID = 10402    AUTH_UNAUTHORIZED = 20401    AUTH_FORBIDDEN = 20403    RESOURCE_NOT_FOUND = 30404    CONFLICT_STATE = 30409    RATE_LIMITED = 40429    INTERNAL_ERROR = 50500  class ApiError(Exception):    """业务异常:携带错误码、描述与详情"""     def __init__(self, code, message, details=None):        self.code = code        self.message = message        self.details = details        super().__init__(message)  def map_http_status(code):    """错误码首位 -> HTTP 状态码映射"""    prefix = code // 10000    table = {1: 400, 2: 401, 3: 404, 4: 429, 5: 500}    return table.get(prefix, 400)  class ErrorHandler:    """统一错误处理:原始异常 -> 标准响应"""     def __init__(self):        self.gc_count = 0     def _build_response(self, err):        self.gc_count += 1        return {            "code": err.code,            "message": err.message,            "details": err.details,            "traceId": uuid.uuid4().hex[:16],            "timestamp": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),        }     def handle(self, exc):        """统一拦截:ApiError直接转换,未知异常兜底"""        if isinstance(exc, ApiError):            return self._build_response(exc)        return self._build_response(            ApiError(ErrorCode.INTERNAL_ERROR, "internal error")        )  def create_order(order):    """模拟AI服务下单接口:校验参数"""    if not isinstance(order, dict):        raise ApiError(ErrorCode.PARAM_INVALID, "order must be object")    if "model" not in order:        raise ApiError(ErrorCode.PARAM_MISSING, "missing model id")    if order["model"] == "gpt-0":        raise ApiError(ErrorCode.RESOURCE_NOT_FOUND, "model not found")    return "inference task created"  if __name__ == "__main__":    handler = ErrorHandler()    try:        create_order({"prompt": "hello"})    except Exception as exc:        resp = handler.handle(exc)        print(json.dumps(resp, ensure_ascii=False, indent=2))        print("HTTP", map_http_status(resp["code"]))
目的:演示错误码定义、HTTP状态码映射与统一错误处理机制,模拟API接口对非法请求返回标准错误响应的完整链路。
  1. class ErrorCode用枚举定义错误码,体现“一码一义”原则,每个码对应唯一错误场景。
  2. PARAM_MISSING = 10400五位错误码,首位1表示参数类,04表示参数缺失类别,00为序号。
  3. def map_http_status(code)通过首位数字查表映射HTTP状态码,体现4xx/5xx语义翻译逻辑。
  4. class ApiError(Exception)自定义业务异常,携带code/message/details三个关键字段。
  5. handler.handle(exc)统一错误处理入口,所有异常都从这里转换为标准响应。
  6. traceId: uuid.uuid4().hex[:16]生成请求追踪标识,方便全链路日志串联排查。
  7. if not isinstance(order, dict)参数类型校验不通过即抛业务异常,由统一处理器兜底。

常见误区

误区“HTTP状态码已经能表达错误了,400就是参数错,500就是服务器错,完全够用,再设计一套错误码纯属多余。”

正解HTTP状态码只有几十个,语义粒度太粗。一个400可能对应“参数缺失”“参数格式错误”“参数超长”“字段类型不匹配”等几十种情况,调用方拿到400根本无从下手。自定义错误码就像菜单编号比“菜名首字母”更精确——10400表示参数缺失、10402表示参数无效,调用方才能精准编程处理。正确做法是两者配合:HTTP状态码做粗粒度分类(4xx还是5xx),错误码做细粒度定位(具体哪个错误)。

为何常见:初学者只接触过HTTP状态码,没经历过大型系统中几十个服务互相调用的场景,自然意识不到粗粒度状态码在复杂业务里远远不够用。

误区“错误码要体现完整的技术栈信息,包含服务名、机房、错误来源、错误细节,位数越多越显得专业,最好用十二位编码。”

正解错误码是给人看、给程序判断的“菜单编号”,不是技术年报。过长的错误码会增加记忆成本和文档负担,甚至超出整数安全范围。优秀的设计是“够用就好”:五位编码,首码分类、中间两位类别、末位序号,配合文档即可覆盖绝大多数场景。就像米其林菜单也不会把每道菜的产地经纬度写进编号里——信息放在详情字段里,而不是堆在编码里。

为何常见:人在学习新概念时容易陷入“越复杂越高级”的误区,忽略了工程设计的本质是简洁可用。

误区“为了前端好处理,后端不管成功失败都返回200,只在响应体里用code字段区分,如此调用方只需要判断code就好了。”

正解这会破坏HTTP语义,让网关、浏览器、监控系统全部失效——负载均衡没法识别5xx做自动摘除,日志系统没法按状态码做告警,第三方调用方也没法用标准HTTP客户端拦截错误。正确做法是让“HTTP状态码”和“业务错误码”各司其职:状态码告诉基础设施该怎么做,错误码告诉业务代码具体哪里错了。就像酒楼既有“退菜单”流程,也有门口保安看的“营业状态牌”,两者缺一不可。

为何常见:很多前端开发者觉得“统一判断code”很省事,却忽略了HTTP状态码在基础设施层的语义作用,属于典型的局部最优思维。

误区“错误码是契约,发布出去之后连描述都不能改,否则就是不专业、不负责。”

正解契约的稳定性不等于永不变化,而是“向后兼容”地变化。旧错误码的含义和映射保持不变,但描述文案可以优化(从“参数错误”改成“order_id不能为空”);废弃的错误码可以标记为deprecated,只是不能删除、不能复用来表示其他含义。这就像菜单上“已售罄”的招牌菜,编号还在但那道菜不再供应——老顾客不会迷路,新菜品也照常上架。硬性“不能改”反而会导致错误码与真实世界脱节,失去表达力。

为何常见:把“稳定”误解为“冻结”,是缺乏版本管理经验的表现,混淆了“承诺不变”和“永不更新”。

误区“返回错误时把Python的traceback、Java的堆栈信息全部塞进details字段,这样调用方可以直接看到底层代码哪里挂了,开发效率最高。”

正解这是严重的安全隐患!堆栈、SQL、内部类名等敏感信息会直接暴露服务端架构,给攻击者提供线索,而且对调用方调试毫无帮助——他们不需要知道你的Pandas在哪个文件崩溃了。正确的details字段应包含“业务层面的补充信息”:哪个参数不合法、哪个ID不存在、限流阈值是多少。真正的技术细节应该记在服务端日志里,通过traceId关联,由你自己的运维团队查阅。

为何常见:开发者从“自己调试视角”出发,容易忽略API面向的是不可信的外部调用方,安全边界意识不足。

面试题

Q1. 设计API错误码时,最重要的原则是什么?

单选题 · easy

  • A.唯一性:一码一义,绝不共用← 正确答案
  • B.随机性:无规律难以猜测
  • C.简短性:位数越短越好
  • D.模糊性:越概括越好

答案:A。错误码的核心价值是让调用方精确定位错误,因此“一码一义”是设计的基石。随机性让码不可读,简短到丢失信息、模糊到无法区分,都会让错误码形同虚设。就像菜单编号若两道菜共用一号,服务员和顾客都会崩溃。

Q2. HTTP状态码 404 对应的错误类型是?

单选题 · easy

  • A.资源不存在,请检查目标← 正确答案
  • B.参数格式错误,请修正
  • C.服务内部故障,请稍后
  • D.请求过于频繁,被限流

答案:A。404 是“Not Found“,语义是请求的资源在服务端不存在,对应错误码体系中的资源类(首码3)。参数错误是400,服务故障是500,限流是429。就像顾客点的”烤鸭“菜单上根本没有,服务员会告诉你”这道菜不存在”。

Q3. 错误码 10402 中首位数字"1"代表什么?

单选题 · mid

  • A.参数类错误,需调用方检查← 正确答案
  • B.鉴权类错误,需重新登录
  • C.资源类错误,需检查目标
  • D.服务端错误,需等待重试

答案:A。按“首位数字即分类”的设计,1 代表参数类错误,说明问题出在调用方传入的请求参数上,映射HTTP 400。2是鉴权类、3是资源类、5是服务类。首位数字就像菜单大区的编号,一看便知该去哪个区找问题。

Q4. 以下哪些属于标准错误响应应包含的字段?

多选题 · mid

  • A.code 机器可读的错误码← 正确答案
  • B.message 人类可读的描述← 正确答案
  • C.traceId 全链路追踪标识← 正确答案
  • D.password 用户明文密码

答案:ABC。标准错误响应五要素是 code、message、details、traceId、timestamp。password 是绝对不能出现在响应中的敏感信息。code让程序做判断,message让开发者看懂,traceId让运维串联日志,三者构成“退菜单”的核心信息。

Q5. 服务端返回 5xx 状态码,正确的处理方式是什么?

单选题 · mid

  • A.服务端处理请求失败,应稍后重试← 正确答案
  • B.客户端参数错误,需要立即修参
  • C.用户身份未通过校验,重新登录
  • D.请求被限流拦截,降低频次

答案:A。5xx 表示服务端自身的故障,比如数据库宕机、AI模型超时。这时候客户端反复重试往往无效,应该采用退避重试策略或直接通知用户稍后再试。4xx才是调用方需要修正自己的错误,两者区分是错误处理的基本功。

Q6. 客户端收到 401 状态码后,最合理的下一步操作是什么?

单选题 · mid

  • A.刷新凭证后重新发起请求← 正确答案
  • B.原样重发一模一样的请求
  • C.忽略错误继续操作界面
  • D.立即清空本地全部数据

答案:A。401 表示鉴权失败,通常是Token缺失或过期,正确的做法是刷新凭证并重试一次,成功后继续原流程。原样重发必然再撞401,忽略错误会导致后续请求全部失效,清空数据则是过度反应。这就像门禁卡失效了,办新卡再刷一次即可。

Q7. 关于错误码向后兼容策略,以下哪些做法是正确的?

多选题 · hard

  • A.已有错误码含义保持永久不变← 正确答案
  • B.新版本可复用旧码表示别的错误
  • C.废弃错误码应保留编号占位← 正确答案
  • D.新增错误码配套文档同步更新← 正确答案

答案:ACD。向后兼容要求已发布错误码的含义和映射永远不变(A对);因此绝不能复用旧码表示新错误(B错);废弃的码应保留占位并标记deprecated(C对),否则历史日志无法解读;新增码必然要更新文档(D对)。这就像菜单改版后,老菜编号永远留位,新菜永远用新号码。

Q8. 统一错误处理机制的核心价值是什么?

单选题 · easy

  • A.所有异常转换标准格式响应← 正确答案
  • B.隐藏系统中所有错误信息
  • C.让每个模块自定义响应格式
  • D.从根本上杜绝错误的发生

答案:A。统一错误处理像一个“值班经理”,把所有内部异常统一拦截、记录、分类、脱敏,最终输出标准格式的响应。它不会杜绝错误,也不该隐藏信息,更不是让各模块各自为政。它的价值在于让错误响应“永远一致、永不裸奔”。

Q9. 设计错误响应格式时,以下做法合理的是?

多选题 · mid

  • A.包含机器可读的标准错误码← 正确答案
  • B.提供人类可读的message描述← 正确答案
  • C.输出完整服务端调用堆栈
  • D.附带traceId方便日志追踪← 正确答案

答案:ABD。错误码让程序做精准分支,message让开发者快速定位,traceId让运维跨服务串起日志,这三者是标准响应必备。输出完整堆栈是严重的安全隐患——内部类和SQL结构会暴露给攻击者,技术细节应留在服务端日志里,只通过traceId关联。

Q10. 错误码从v1演进到v2版本时,在稳定错误码集合上必须满足什么?

单选题 · extreme

  • A.映射函数保持完全一致← 正确答案
  • B.所有错误码全部重新编号
  • C.立即删除废弃的旧错误码
  • D.与HTTP状态码完全等价合并

答案:A。v2的映射函数在稳定错误码集合上必须等于v1,即 f_v2(c)=f_v1(c),所有旧含义不变,新错误只占用全新编号。重新编号、删除旧码都会让老调用方崩溃;错误码与HTTP状态码维度不同,也不能合并。稳定集合上的函数恒等,才是“菜单加厚但老菜不挪位”的数学表达。