1. 背景
HTTP API 成为了构建现代 Web 应用程序和移动应用程序的常用选择,因为它简单、灵活且易于理解。
对于正常响应,网络上有无数的文章讲过,不赘述。但是对于返回错误信息,并没有形成共识。以下是一些常见的做法:
- HTTP 状态码(HTTP Status Code):使用标准的 HTTP 状态码来表示请求的处理结果。常见的状态码有:
- 2xx 成功:表示请求被成功处理,如 200 OK 表示成功返回请求的内容。
- 4xx 客户端错误:表示客户端发送的请求有问题,如 400 Bad Request 表示无效的请求。
- 5xx 服务器错误:表示服务器在处理请求时发生错误,如 500 Internal Server Error 表示服务器内部错误。
通过适当选择合适的状态码,可以清楚地表示请求的结果和错误的类型。
- 响应主体(Response Body):除了使用状态码,还可以在响应的主体中包含详细的错误信息。可以使用 JSON 或其他格式来构造错误响应体,其中包含有关错误的更多信息,如错误代码、错误消息、错误描述等。通过在响应主体中提供详细的错误信息,客户端可以更好地理解发生的错误,并采取适当的处理措施。 例如,对于 400 错误,可以返回如下的 JSON 格式的错误响应体:
{
"error":{
"code":400,
"message":"Invalid request",
"details":"The request body is missing a required parameter."
}
}
举几个常用的状态码及其意义的例子:
状态码 | 状态名 | 建议场景 |
---|---|---|
200 | OK | 成功 |
201 | Created | 资源被创建 |
400 | Bad Request | 请求参数不合法 |
401 | Unauthorized | 未登录或登录失效 |
403 | Forbidden | 没有访问权限 |
422 | Unprocessable Entity | 请求体验证错误 |
500 | Internal Server Error | 服务器发生错误 |
今天讨论的重点是响应主体的格式,它可是百花齐放的,举几个业界例子:
1.1. Amazon SP-API
来源: https://developer-docs.amazon.com/sp-api/docs/response-format#error-response
{
"errors":[
{
"message":"Access to requested resource is denied.",
"code":"Unauthorized",
"details":"Access token is missing in the request header."
}
]
}
Amazon 这个 API 返回的是错误对象数组,可以支持返回多个错误。
1.2. Azure REST API
来源: https://github.com/microsoft/api-guidelines/blob/vNext/azure/Guidelines.md
{
"error":{
"code":"InvalidPasswordFormat",
"message":"Human-readable description",
"target":"target of error",
"innererror":{
"code":"PasswordTooShort",
"minLength":6
}
}
}
Azure 这个 API 在发生错误的时候,返回一个 ErrorDetail
对象,这是很常见的一种做法。
1.3. Google Cloud API
来源: https://cloud.google.com/apis/design/errors
{
"error":{
"code":400,
"message":"API key not valid. Please pass a valid API key.",
"status":"INVALID_ARGUMENT",
"details":[
{
"@type":"type.googleapis.com/google.rpc.ErrorInfo",
"reason":"API_KEY_INVALID",
"domain":"googleapis.com",
"metadata":{
"service":"translate.googleapis.com"
}
}
]
}
}
Google API 设计指南建议的格式,是一个 Error.Status
对象。
1.4. Twitter/X API
来源: https://developer.twitter.com/en/support/x-api/error-troubleshooting
{
"client_id":"101010101",
"required_enrollment":"Standard Basic",
"registration_url":"[https://developer.twitter.com/en/account](https://developer.twitter.com/en/account)",
"title":"Client Forbidden",
"detail":"This request must be made using an approved developer account that is enrolled in the requested endpoint. Learn more by visiting our documentation.",
"reason":"client-not-enrolled",
"type":"[https://api.twitter.com/2/problems/client-forbidden](https://api.twitter.com/2/problems/client-forbidden)"
}
Twitter 的错误格式,虽然没明说,其实就是下面要介绍的 RFC 7807/9457。
由此可见,各家都有自己定义的标准。但是其实标准化组织 IANA 已经定义了标准。
2. RFC 9457:Problem Details for HTTP APIs
2.1. 先说 RFC 7807
RFC 7807 是最初的版本。
RFC 7807 的目标是提供一种一致的方式来描述和传递 API 中的错误,以提高互操作性和开发人员的开发体验。它定义了一个标准的 JSON 格式,用于表示问题详情(Problem Details),包括错误代码、错误消息、错误描述等。
以下是 RFC 7807 中定义的 Problem Details 的基本结构:
HTTP/1.1 403 Forbidden
Content-Type: application/problem+json
Content-Language: en
{
"type":"[https://example.com/probs/out-of-credit](https://example.com/probs/out-of-credit)",
"title":"You do not have enough credit.",
"detail":"Your current balance is 30, but that costs 50.",
"instance":"/account/12345/msgs/abc",
"balance":30,
"accounts":[
"/account/12345",
"/account/67890"
]
}
字段解释
- type:字段表示问题类型,可以是一个 URL,用于唯一标识问题的类型。例如:https://example.com/validation-error.
- title:人类可读的标识符,通常相同的类型字段应该有相同的标题字段旁边。一个例子是
Form validation failed
。 - status:返回 HTTP 状态码,和实际状态码保持一致。
- detail:关于具体问题的更多信息,如果可能,还有纠正它的步骤。例如,关于表单验证问题的信息用户名已被占用,请选择不同的用户名。
- instance:此特定事件的标识符,它可能对客户端没有用处,但可用于问题排查。
- 更多字段:可以添加任何字段来提供额外的信息,客户端可以忽略它们不支持的字段。
使用 RFC 7807 标准,API 可以以统一的方式返回错误和问题信息,使得客户端能够更好地理解和处理这些问题。该标准还提供了一些推荐的扩展字段,用于支持更多的自定义属性,以满足特定应用或领域的需求。
通过采用 RFC 7807 标准,API 开发人员可以提供一致和规范的错误处理机制,提高 API 的可用性、可维护性和互操作性。同时,客户端开发人员也可以更轻松地处理和解析 API 返回的问题信息。
建议还是去看看 RFC 原文: https://www.rfc-editor.org/rfc/rfc7807
2.2. RFC 9457
RFC 9457 是 RFC 7807 的更新版,是会替换 RFC 7807 的。
相对于 RFC 7807,它在继承所有字段的基础上,增加了 errors
错误信息数组,结构如下:
HTTP/1.1 422 Unprocessable Content
Content-Type: application/problem+json
Content-Language: en
{
"type":"[https://example.net/validation-error](https://example.net/validation-error)",
"title":"Your request is not valid.",
"errors":[
{
"detail":"must be a positive integer",
"pointer":"#/age"
},
{
"detail":"must be 'green', 'red' or 'blue'",
"pointer":"#/profile/color"
}
]
}
这个 errors
字段主要用来描述验证错误,所以需要是一个数组。
errors
字段解释
- detail:问题的描述。
- pointer:使用 RFC 6901 定义的 JSON Pointer 指向请求的错误字段。
另外,因为 type
是一个 URL 字段,实际上这个字段可以涵盖多家厂商的错误类型,所以,IANA 建立了一个共享注册表,用来记录多家厂商的错误类型。但是目前为止,这个错误注册表还没有太多的信息。
错误注册表网址: https://www.iana.org/assignments/http-problem-types/http-problem-types.xhtml
更多信息请参考 RFC 原文: https://www.rfc-editor.org/rfc/rfc9457
3. 总结
这个标准还是挺话痨的。但既然有了标准,在设计新的 API 的时候,就尽量使用吧,在 IT 界,有时候的问题就是选择太多了,造成了混乱。与其手搓一个,还不如遵从一个标准。
想起了巴别塔的故事:
巴别塔的传说源自圣经《创世纪》第 11 章。根据传说,当时地球上的人类都使用同一种语言,并聚集在一起建造一座高耸入云的塔,被称为巴别塔(Tower of Babel)。人类的目的是建造一座塔,以达到天堂,并使他们的名字在整个地球上得到认可。然而,上帝看到人类的行为,并决定干涉他们的计划。为了阻止人类完成巴别塔,上帝使人类的语言变得混乱,使他们无法相互理解。人们开始说不同的语言,导致他们无法有效沟通和合作。由于语言的混乱,工程停顿,人们分散到世界各地,形成了不同的民族和语言群体。因此,巴别塔的传说被视为上帝对人类过于自负和傲慢的行为的惩罚,同时也解释了为什么世界上存在着不同的语言和文化。