摘要

  • 2016 年 3 月发布的 RFC 7807 为 HTTP API 错误定义统一的 JSON 与 XML 形式,并把 type URI 明确设为机器识别错误类型的主要标识。
  • 2023 年 7 月的 RFC 9457 取代前者,进一步说明相对 URI、多种错误同现,以及日后更换类型地址带来的身份断裂。
  • IANA 的正式登记表 收录可共用的错误类型。省略 type 时默认的 about:blank,只表达 HTTP 状态码原有的含义。

一个状态码,答不出商店的问题

浏览器、代理和通用 HTTP 程序看到 403,知道请求遭到拒绝。购物客户端还要问:账户余额不足,还是交易碰上另一条规则?如果每家服务都为了自己的业务创建新状态码,通用 HTTP 语义很快会被私有业务词汇挤碎。RFC 7807 给出另一种分工:状态码继续说明广义结果,响应正文再说明该应用碰到哪一种问题。

标准的示例让购买请求收到 403,同时给出余额不足的类型、这一回失败的说明、单次事件的地址,以及余额与充值账户等扩展字段。application/problem+json 提供统一外壳;同一份标准也定义 XML 形式。外壳里的具体错误语义,仍由发布 API 的机构负责。标准替它安排了位置,没有替它决定“余额不足”到底指什么、客户下一步该怎么做。

关键字段是 type。2016 年文本要求消费者把它的 URI 当作问题类型的主要身份。title 是供人阅读的短标题,可以随语言变化;detail 解释这一次发生的事,不适合被程序拆词当协议使用。一个靠搜索“余额”两字决定动作的程序,连修改文案都扛不住。一个识别稳定 URI 的程序,至少清楚知道自己依赖的是哪一项公开承诺。

另一个地址 instance 不负责这项承诺。它标识某一次出错。两个用户都余额不足,可以共享同一个 type,却各有一次事件的 instance。客服可能需要后者查单;客户端需要前者选择处理方式。把两者笼统叫作“错误 ID”,等于把可重复识别的类别和一次性票据混在一起。

一个故意不多说的空白类型

如果响应没有写 type,规范把它视为 about:blank。这个名字看似寒酸,却划清了边界:它不增加状态码以外的语义。一个带有 about:blank 的 404 还是 404。JSON 格式再整齐,也不会自动长出可供机器判断的新类别。运营者要让客户端区别两种 403,就得明确命名、解释并维护区别。

正文中的 status 同样不是 HTTP 响应行的替身。规范说它只是提示;若写入,发送者必须让它与实际响应状态一致。中间设备可能改变状态,日志也可能只保存正文,但通用 HTTP 软件仍看实际状态。错误对象补足业务细节,没有接管承载它的协议层。

2023 年,把改地址的账算清楚

RFC 9457 在 2023 年继承这套结构,同时把 URI 稳定性说得更明白。像 example-problem 这样的相对地址,会随响应页面的基准地址而解析成不同资源;同一串字可能指向两种错误。因此新版建议尽可能使用绝对 URI。

规范允许不能打开的类型 URI。问题出在后续迁移:如果运营者日后想让工具访问说明文档,换成一个可打开的新地址,机器看到的就是另一种类型。旧客户端未必认得。设计会议上一句“先随便取个名字”,会变成未来的版本兼容成本。这不是 URL 美观问题,而是身份连续性问题。

新版还处理多个错误同时出现的情况。一次验证失败可以通过扩展字段列出几个不合格输入;若出现互不相同的错误类型,规范建议呈现最相关或最紧急的一种。单个 HTTP 响应只有一个主状态,杂乱堆放各类失败会让客户端失去处理顺序。新版建立的 IANA 类型登记表 服务于可复用的公共类型,要求有正式规格;某家公司的专属错误仍归它自己命名和维护。

要计算真实部署范围,还缺有日期的服务响应和客户端版本记录。两版 RFC 已经留下清晰的制度分工:标准组织提供共同语法,API 运营者给具体失败命名,并对依赖这个名字的软件负责。