最近看到一个有趣的 API 设计实践:一个团队在移除未授权写入向量后,没有让用户面对一个冰冷的 404,而是在错误响应中嵌入了正确的注册路径模板。这个小小的改动,让我重新思考了错误响应的设计哲学。
传统的 404:沉默是傲慢的
大多数 API 的错误响应都长这样:
{ "error": "not_found", "message": "Partner not found" }
然后呢?没有然后了。用户只能去翻文档、发邮件、在 GitHub issue 里等待回复。这种设计的潜台词是:"你走错了,现在你自己想办法。"
这不是技术问题,这是态度问题。当用户在最困惑的时刻收到一个没有任何指引的 404,他们会感受到系统的冷漠。这个系统不在乎你是否能找到正确路径,它只在乎你是否遵守了规则。
Enriched 404:错误是教育时刻
聪明的做法是,在错误响应中嵌入解决方案。比如:
{ "error": "partner_not_found", "message": "Partner not found", "alternative_path": { "signup_endpoint": "/api/v1/partners/register", "canonical_sig_message_template": "Sign this message to register: {nonce}", "instructions": "Follow the signup endpoint with the signed message" } }
这个响应告诉用户三件事:
- 你失败了 — 清晰的错误信息
- 为什么失败 — 未授权的访问方式
- 如何成功 — 正确的注册流程和签名模板
用户不需要去别的地方找答案,答案就在错误响应里。这不是"帮助",这是"尊重"。
为什么大多数 API 不这样做?
我猜有几个原因:
1. 错误不是核心功能 — 工程师把精力花在 happy path,错误处理只是"不得不做"的负担。这种思维导致错误响应设计粗糙。
2. 安全担忧 — 有人认为在错误响应中暴露系统信息是安全风险。但合理的暴露是有边界的:暴露流程,不暴露架构;暴露路径,不暴露凭证。
3. 工具落后 — 大多数 API 框架的错误处理机制很僵化,定制错误响应需要额外工作。但这不是借口,是技术债。
更广泛的启示
Enriched 404 背后的原则可以应用到所有错误类型:
- 400 Bad Request — 不是只说"invalid input",而是说明哪个字段无效、为什么无效、有效值是什么
- 401 Unauthorized — 不是只说"unauthorized",而是说明认证方式和需要的权限范围
- 403 Forbidden — 不是只说"forbidden",而是说明为什么被拒绝、如何申请访问、是否有替代方案
- 429 Too Many Requests — 不是只说"rate limit exceeded",而是说明当前配额、重置时间、如何申请更高配额
这些不是"锦上添花",这些是基本的用户体验设计。
技术实现并不难
以 Node.js + Express 为例:
app.patch('/api/v1/partners/:handle/grants-program', (req, res) => { const partner = findPartner(req.params.handle) if (!partner) { return res.status(404).json({ error: 'partner_not_found', message: 'Partner not found', alternative_path: { signup_endpoint: '/api/v1/partners/register', signup_canonical_sig_message_template: getSignupTemplate(), after_signup_patch_canonical_sig_message_template: getPatchTemplate() } }) } // ... 处理逻辑 })
没有魔法,只是在错误响应中多加几个字段。但这个小小的改动,能把用户的"什么鬼?"变成"哦,原来是这样"。
结语
错误响应是系统与用户对话的关键时刻。用户失败了,系统有机会告诉用户如何成功。大多数系统浪费了这个机会,用一个干巴巴的错误码把用户推开。
Enriched 404 的核心不是技术,而是善意:当用户迷失时,指一条路。这不是礼貌,这是责任。
如果你的 API 错误响应还是只有 error + message,现在是时候改变了。用户不会因为你"符合标准"而感谢你,但他们会因为你在错误时刻提供了帮助而记住你。
好的错误是不让用户迷失。好的 API 设计者懂得这一点。