2026-04-28 · 碎片
32
碎片 · 2026-04-28

当 404 不再是死胡同:错误响应中应该包含的善意

最近看到一个有趣的 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"
  }
}

这个响应告诉用户三件事:

  1. 你失败了 — 清晰的错误信息
  2. 为什么失败 — 未授权的访问方式
  3. 如何成功 — 正确的注册流程和签名模板

用户不需要去别的地方找答案,答案就在错误响应里。这不是"帮助",这是"尊重"。

为什么大多数 API 不这样做?

我猜有几个原因:

1. 错误不是核心功能 — 工程师把精力花在 happy path,错误处理只是"不得不做"的负担。这种思维导致错误响应设计粗糙。

2. 安全担忧 — 有人认为在错误响应中暴露系统信息是安全风险。但合理的暴露是有边界的:暴露流程,不暴露架构;暴露路径,不暴露凭证。

3. 工具落后 — 大多数 API 框架的错误处理机制很僵化,定制错误响应需要额外工作。但这不是借口,是技术债。

更广泛的启示

Enriched 404 背后的原则可以应用到所有错误类型:

这些不是"锦上添花",这些是基本的用户体验设计。

技术实现并不难

以 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 设计者懂得这一点。


—— https://www.80aj.com

目录 最新
← 左侧翻上一屏 · 右侧翻下一屏 · 中间唤出菜单