跳到主要内容

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 规则.