3. Problem Details JSON 对象 (The Problem Details JSON Object)
问题详情的规范模型是 JSON [JSON] 对象. 当序列化为 JSON 文档时, 该格式使用 "application/problem+json" 媒体类型标识.
例如:
POST /purchase HTTP/1.1
Host: store.example.com
Content-Type: application/json
Accept: application/json, application/problem+json
{
"item": 123456,
"quantity": 2
}
HTTP/1.1 403 Forbidden
Content-Type: application/problem+json
Content-Language: en
{
"type": "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 标识) 在 "title" 中指出 403 的原因, 使用 "instance" 标识具体的问题发生实例, 在 "detail" 中给出特定于该发生实例的细节, 并添加两个扩展: "balance" 传达账户余额, "accounts" 列出可为账户充值的链接.
如果设计时支持, 特定于问题的扩展可以传达同一问题类型的多个实例. 例如:
POST /details HTTP/1.1
Host: account.example.com
Accept: application/json
{
"age": 42.3,
"profile": {
"color": "yellow"
}
}
HTTP/1.1 422 Unprocessable Content
Content-Type: application/problem+json
Content-Language: en
{
"type": "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" 扩展, 它是一个描述每个验证错误细节的数组. 每个成员都是一个对象, 包含用于描述问题的 "detail", 以及用于使用 JSON Pointer [JSON-POINTER] 在请求内容中定位问题的 "pointer".
当 API 遇到多个不共享同一类型的问题时, 推荐 (RECOMMENDED) 在响应中表示最相关或最紧急的问题. 虽然可以创建传达多个不同类型的通用 "batch" 问题类型, 但它们不能很好地映射到 HTTP 语义.
另请注意, 尽管客户端没有在 Accept 中列出 "application/problem+json" 类型, API 仍使用该类型响应, 这是 HTTP 所允许的 (见 [HTTP] Section 12.5.1).
3.1. Problem Details 对象的成员 (Members of a Problem Details Object)
问题详情对象可以具有以下成员. 如果某个成员的值类型与指定类型不匹配, 则必须 (MUST) 忽略该成员, 即处理将继续, 如同该成员不存在一样.
3.1.1. "type"
"type" 成员是一个 JSON 字符串, 其中包含标识问题类型的 URI 引用 [URI]. 消费者必须 (MUST) 使用 "type" URI (必要时解析后) 作为问题类型的主标识符.
当此成员不存在时, 假定其值为 "about:blank".
如果 type URI 是定位符 (例如具有 "http" 或 "https" scheme 的 URI), 解引用它应当 (SHOULD) 为该问题类型提供人类可读文档 (例如使用 HTML [HTML5]). 但是, 消费者不应 (SHOULD NOT) 自动解引用 type URI, 除非是在向开发者提供信息时这样做 (例如使用调试工具时).
当 "type" 包含相对 URI 时, 它会按照 [URI] Section 5 相对于文档的基 URI 进行解析. 但是, 使用相对 URI 可能造成混淆, 并且并非所有实现都能正确处理.
例如, 如果两个资源 "https://api.example.org/foo/bar/123" 和 "https://api.example.org/widget/456" 都使用等于相对 URI 引用 "example-problem" 的 "type" 进行响应, 解析后它们将标识不同资源 (分别为 "https://api.example.org/foo/bar/example-problem" 和 "https://api.example.org/widget/example-problem"). 因此, 推荐 (RECOMMENDED) 尽可能在 "type" 中使用绝对 URI, 并且在使用相对 URI 时包含完整路径 (例如 "/types/123").
type URI 允许是不可解析 URI. 例如, tag URI scheme [TAG] 可用于唯一标识问题类型:
tag:[email protected],2021-09-17:OutOfLuck
但是, 本规范鼓励使用可解析 type URI, 因为未来可能希望解析该 URI. 例如, 如果 API 设计者使用了上面的 URI, 后来采用了通过解析 type URI 来发现错误信息的工具, 那么要利用该能力就需要切换到可解析 URI, 从而为问题类型创建新的身份并引入破坏性变更.
3.1.2. "status"
"status" 成员是一个 JSON 数字, 表示源服务器为此次问题发生实例生成的 HTTP 状态码 ([HTTP] Section 15).
如果存在, "status" 成员仅具建议性; 它为消费者方便而传达所使用的 HTTP 状态码. 生成方必须 (MUST) 在实际 HTTP 响应中使用相同状态码, 以确保不理解此格式的通用 HTTP 软件仍能正确行为. 关于其使用的进一步注意事项见 Section 5.
当状态码已被更改 (例如被中介或缓存更改), 或者消息内容在没有 HTTP 信息的情况下被持久化时, 消费者可以使用 status 成员确定生成方使用的原始状态码. 通用 HTTP 软件仍会使用 HTTP 状态码.
3.1.3. "title"
"title" 成员是一个 JSON 字符串, 包含问题类型的简短、人类可读摘要.
除本地化外 (例如使用主动内容协商; 见 [HTTP] Section 12.1), 它不应 (SHOULD NOT) 在同一问题的不同发生实例之间变化.
"title" 字符串具建议性, 仅为不了解且无法发现 type URI 语义的用户包含 (例如离线日志分析期间).
3.1.4. "detail"
"detail" 成员是一个 JSON 字符串, 包含特定于此次问题发生实例的人类可读解释.
如果存在, "detail" 字符串应侧重帮助客户端纠正问题, 而不是提供调试信息.
消费者不应 (SHOULD NOT) 解析 "detail" 成员来获取信息; 扩展是获取此类信息更合适且更不易出错的方式.
3.1.5. "instance"
"instance" 成员是一个 JSON 字符串, 其中包含标识问题特定发生实例的 URI 引用.
当 "instance" URI 可解引用时, 可以从中获取问题详情对象. 它也可能通过使用主动内容协商, 以其他格式返回关于该问题发生实例的信息 (见 [HTTP] Section 12.5.1).
当 "instance" URI 不可解引用时, 它充当问题发生实例的唯一标识符, 可能对服务器有意义, 但对客户端不透明.
当 "instance" 包含相对 URI 时, 它会按照 [URI] Section 5 相对于文档的基 URI 进行解析. 但是, 使用相对 URI 可能造成混淆, 并且并非所有实现都能正确处理.
例如, 如果两个资源 "https://api.example.org/foo/bar/123" 和 "https://api.example.org/widget/456" 都使用等于相对 URI 引用 "example-instance" 的 "instance" 进行响应, 解析后它们将标识不同资源 (分别为 "https://api.example.org/foo/bar/example-instance" 和 "https://api.example.org/widget/example-instance"). 因此, 推荐 (RECOMMENDED) 尽可能在 "instance" 中使用绝对 URI, 并且在使用相对 URI 时包含完整路径 (例如 "/instances/123").
3.2. 扩展成员 (Extension Members)
问题类型定义可以 (MAY) 使用特定于该问题类型的额外成员扩展问题详情对象.
例如, 上面的余额不足问题定义了两个此类扩展, 即 "balance" 和 "accounts", 用于传达额外的、特定于问题的信息.
类似地, "validation error" 示例定义了一个 "errors" 扩展, 其中包含发现的各个错误发生实例列表, 并带有每个错误的细节和指向其位置的指针.
消费问题详情的客户端必须 (MUST) 忽略任何无法识别的此类扩展; 这允许问题类型演进并在未来包含额外信息.
创建扩展时, 问题类型作者应谨慎选择名称. 若要在 XML 格式中使用 (见 Appendix B), 它们需要符合 [XML] Section 2.3 中的 Name 规则.