用途
awesome-error-standards 用于在 API 或应用中实施一致的错误处理模式,覆盖错误的抛出、捕获、记录与对外呈现,使失败可预测、可调试,同时不泄露内部实现细节。
主要能力
- 错误分类:先区分操作性错误(网络超时、无效输入、记录缺失等预期失败)与程序员错误(未定义访问、不变量被破坏等缺陷)。操作性错误应处理、重试或呈现;程序员错误应大声崩溃或重启进程,而不是带着损坏状态继续运行。
- 预防优先:让错误状态在类型系统中不可表示(可辨识联合、品牌/不透明类型),在边界校验,使用运行时守卫与错误边界。对高价值不变量在多层防护,并测试绕过外层后内层仍能捕获。
- 修复分级:控制(错误无法发生)、警告(发生时立即暴露,如编译错误、校验失败、lint 规则)、检测(事后发现,如测试、监控、告警)。注释或“小心”不算修复;监控不是预防。
- 启动时失败:在初始化阶段校验所有必需配置、环境变量与连接,失败则拒绝启动,而不是在首个请求时才发现缺失值。
- 快速失败:尽早校验输入与前置条件,抛出或返回带清晰消息的错误,不带着无效状态继续。
- 不吞掉错误:避免空 catch 或忽略一切的宽泛 catch;应记录并/或重新抛出,或返回结果类型,并携带上下文传播。
- 结构化错误:API 错误与日志使用一致形状(如 code、message、details);类型化错误类或错误码便于调用方按类型处理。
- 用户与开发者分离:用户可见消息安全且可操作;开发者细节(堆栈、内部消息、请求 id)只进日志或调试,绝不向终端用户暴露堆栈或 SQL。
使用方式
工作流程:
- 识别边界:错误从何而来(校验、数据库、外部 API、认证),在何处处理(路由处理器、中间件、顶层)。确保每层要么处理,要么带上下文传播。
- 定义错误形状:API 使用 HTTP 状态 + 响应体信封(code、message、可选 details);代码使用错误类或结果类型。若项目已有模式则沿用。
- 映射到 HTTP:校验 → 400 或 422;认证 → 401;授权 → 403;未找到 → 404;冲突 → 409;服务器 → 500。不要用 200 加 success: false 表示错误。校验失败在 400 与 422 中只选一种风格,记录并应用到所有端点。
- 先记录再响应:以适当级别(error/warn)记录带上下文(请求 id、安全时的用户 id、操作)的错误,然后返回用户安全响应。
- 校验与净化:在 API 边界使用 schema(Zod、Pydantic 等),以一致格式返回字段级校验错误。
API 错误响应标准信封示例:
{ "error": { "code": "validation_error", "message": "Request validation failed", "details": [ { "field": "email", "message": "Must be a valid email address", "code": "invalid_format" } ] } }
- code:机器可读(如 validation_error、not_found、rate_limit_exceeded),客户端可据此分支。
- message:人类可读,可安全展示给用户,不含堆栈或内部路径。
- details:可选;校验时列出字段级错误,通用 500 可省略。
- request_id / trace_id:可选,放在信封或响应头中供支持使用,不暴露内部信息。
HTTP 状态:400 Bad Request、401 Unauthorized、403 Forbidden、404 Not Found、409 Conflict、422 Unprocessable Entity、429 Too Many Requests、500 Internal Server Error。按语义使用,不用 200 表示错误。
客户端重试失败调用:
- 永不重试(应修复请求):400、401、403、404、415、多数 422。
- 修改请求后重试:402(充值/改套餐)、部分 422(调整载荷),不是盲目重试。
- 带退避重试:429、500、502、503、504 等瞬态错误。限制尝试次数(约 5 次)与总等待(约 30 秒),使用带抖动的指数退避避免惊群。
- 规则:尊重服务器信号,优先遵循 Retry-After 与 X-RateLimit-Reset/X-RateLimit-* 头;对重试的状态变更请求使用 Idempotency-Key,避免重复扣费或重复创建;流式(SSE/websocket)错误以流内事件或终止帧到达,而非 HTTP 状态,因为流打开时状态已是 200,需显式处理流的错误通道。
代码模式:
- 类型化错误(推荐):
class NotFoundError extends Error {
constructor(resource: string) {
super(${resource} not found);
this.name = 'NotFoundError';
}
}
// Caller: catch (e) { if (e instanceof NotFoundError) return res.status(404).json(...); }
- 分层处理:低层抛出或返回 Result;路由处理器或中间件捕获并映射到 HTTP + 日志。不要每层都捕获,应传播并在边界处理。
- 异步:在 async 函数中使用 try/catch;确保 promise 拒绝被处理(全局处理器或 .catch),以便记录而非成为未处理拒绝。为 unhandledRejection/uncaughtException(或平台等价物)注册边界处理器:带上下文记录,程序员错误则退出,不要在损坏状态上恢复。
日志记录:
- 级别:需要关注的失败用 error;可恢复(重试、回退)用 warn。
- 内容:记录消息、错误类型、堆栈(仅服务端)、request_id。不要记录机密或完整 PII。
- 一次:在处理的边界记录,避免每层重复记录同一错误。
- 安全序列化:日志/错误路径使用安全序列化器(safeStringify、结构化日志自带序列化器),而非原始 JSON.stringify——后者遇循环引用和 BigInt 会抛错,可能让正在报告失败的处理器崩溃。
规则:
- 沿用项目现有模式(错误类、响应形状、日志)。若不存在,引入最小一致模式并记录。
- 不向终端用户暴露内部细节(堆栈、数据库错误、文件路径),仅在服务端记录。
- 校验错误返回 400/422 及字段级细节,而非 500。
静默失败:最难的一类——开始正常、返回 200、却悄悄什么都没做(或做错)。按顺序诊断:未接线/未注册组件 → 未定义引用解析为空操作 → 类型/信号不匹配丢弃载荷 → 被吞掉的错误(catch {}、|| true)→ 上游采样/过滤使下游指标静默错误。不要因为某个键被拒绝就断定功能不存在——发布降级配置前先查该组件自己的键,并披露任何工具/方法替换,而不是把原方法报告为缺失。
检查清单:
- 输入在边界校验;校验错误以标准形状返回
- 错误映射到正确 HTTP 状态(不用 200 表示错误)
- 用户可见消息安全可操作;细节仅在日志
- 响应前记录带上下文(请求 id、操作)的错误
- 无空 catch;要么处理、记录并重抛,要么返回结果类型
反模式与更好做法:
- 200 OK 加 { success: false, error: "..." } → 使用正确 HTTP 状态(4xx/5xx)与错误体
- 空 catch 块 → 记录并重抛,或返回 Err(result);若有意忽略则记录说明
- 丢弃错误:Go 的 _ := f()、Python 的裸 except:、未检查的 Close() → 处理错误,或就地说明忽略为何安全
- 库代码中对操作性错误使用 panic() / process.exit() → 返回或抛出类型化错误,由边界决定
- 出错时记录完整请求/响应 → 记录 id 与摘要,脱敏机密与 PII
- 向客户端暴露堆栈 → 服务端记录堆栈,向客户端返回通用消息
- 每个端点错误形状不同 → 全 API 使用一个信封(code、message、details)
- 传输感知的领域错误:服务或仓储返回/抛出框架响应对象或 HTTP 状态码 → 抛出与传输无关的类型化错误,由唯一全局处理器映射到 HTTP,业务逻辑与交付解耦,更易复用与测试
适用场景
- 在 API 或应用中实现或重构错误处理
- 用户要求“错误处理”“如何处理错误”或“一致错误”
- 审查代码的失败处理是否恰当
- 为服务或 API 定义或记录错误契约
限制
资料未提供该技能的具体安装命令、平台支持范围、收费或价格信息,暂未提供。