前言
错误处理是 Web 开发中普遍又重要的一个环节,是指前端通过 RESTFul 风格的 API 调用后端服务,而在 API 执行时,可能产生种种错误,包括参数不正确、代码逻辑错误或者第三方服务不可用。当错误发生时,需要前后端共同协作,处理好善后工作。本文旨在设计一套简洁实用的错误处理机制以指导前后端进行开发。涉及到错误的产生、转换、编码、显示、定位等多个方面,在设计过程中,主要考虑了以下问题:
- 应用程序错误、第三方库、服务产生的错误都应被妥善处理,不可遗漏
- 兼容 HTTP 规范
- 对程序和用户都保持友好
- 完整但简单
- 优雅的编码方式
- 便于定位问题
总则
客户端收到服务端返回的 HTTP response,需要关注两个地方:status code 和 body。若 status code 等于 200,表示这个请求被成功执行,body 中承载了预期的数据,客户端按照特定的业务逻辑处理即可。除此之外的所有 status code 皆表示发生了某种异常,此时 response 的 body 中会包含 json 格式的 code(Int) 和 message(String) 字段,用来指示错误的详细情况。
message 是对错误的描述,可能为空字符串,该描述一般来自于第三方库或开发者抛出异常时指定的 message,这个消息是给程序员看的,只作为前后端定位问题的工具,不应将其暴露给用户。
code 指示了错误的类型,前后端需要对每一个 code 代表的意义达成共识,当错误发生时,前端需要将该 code 转换为合适的消息显示给用户。通常情况下 code 的值为 0,表示无法提供具体的错误消息或者可以从 status code 以及上下文推断出来,或者发生了系统级别的错误,无法为用户展现有意义的消息。只有当错误是逻辑层面的并且可由用户主动避免,提供 code 才是有意义的。
以下列出了除 200 以外所有可能出现的 status code 以及相关的 code、message 的可能取值:
Status | 一般诱因 | Code | Message |
---|---|---|---|
400 | 客户端提供的参数不正确(一般由 spring 抛出异常),或逻辑不正确(由开发者抛出异常) | >=0 | 对错误的描述 |
401 | 匿名用户访问了需要认证的资源,一般由 spring security 抛出 | 0 | 由框架提供 |
403 | 访问了未授权的资源,一般由 spring security 抛出,或开发者抛出相关异常 | 0 | 由框架或开发者提供,需要注意安全类错误不得提供详细信息 |
404 | 由框架(如接口未找到)或开发者(如ID指定的资源不存在)抛出 | 0 | 可能有值,指示不存在资源的类型 |
500 | 服务端逻辑错误、第三方服务错误导致的未捕获异常 | 0 | 异常的 message |
后端规范
- 使用异常作为错误处理的主要手段,不要在编码中使用错误码、枚举等技术
- 不需要包装框架或第三方抛出的异常,仅在必要的条件下这么做,所有的异常都应一视同仁,并确保最终得到处理
- 充分利用框架或系统提供的异常,以减少自定义异常的数量
- 为每个 HTTP 请求随机唯一的 Id,关联在日志中,并附带在对应 HTTP 相应的 X-Request-Id 头部。此外,如果请求中包含了 X-Request-Id 头,则应使用该 Id。该机制保证错误能被快速地定位到
- 发生异常时,应在日志中详细记录,尤其是未捕获异常以及怀疑遭受安全攻击时
前端规范
- 对用户输入参数做充分校验
- 妥善处理异常状况,根据 status、code 以及上下文为用户提供友好的提示
- 提示错误时,应同时显示 X-Request-Id,这样可以利用用户提供的报错截图快速定位问题
“RESTFul API 错误处理规范”上的一条回复
X-Request-Id的显示是指Console level还是UI level?