3. The Problem Details JSON Object (问题详情 JSON 对象)
问题详情的规范模型是一个 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"]
}
在这里, out-of-credit 问题 (由其 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) 在响应中表示最相关或最紧急的问题。虽然可以创建传达多个不同类型的通用"批处理"问题类型, 但它们不能很好地映射到 HTTP 语义中。
另请注意, API 使用 "application/problem+json" 类型进行了响应, 即使客户端在 Accept 中未列出它, 这是 HTTP 允许的 (参见 [HTTP] 的第 12.5.1 节)。
3.1. Members of a Problem Details Object (问题详情对象的成员)
问题详情对象可以具有以下成员。如果成员的值类型与指定的类型不匹配, 则必须 (MUST) 忽略该成员 -- 即, 处理将继续进行, 就像该成员不存在一样。
3.1.1. "type"
"type" 成员是一个包含 URI 引用 [URI] 的 JSON 字符串, 用于标识问题类型。消费者必须 (MUST) 使用 "type" URI (如有必要, 在解析后) 作为问题类型的主要标识符。
当此成员不存在时, 其值假定为 "about:blank"。
如果 type URI 是定位符 (例如, 具有 "http" 或 "https" 方案的 URI), 则解引用它应该 (SHOULD) 为问题类型提供人类可读的文档 (例如, 使用 HTML [HTML5])。但是, 消费者不应该 (SHOULD NOT) 自动解引用 type URI, 除非它们在向开发人员提供信息时这样做 (例如, 当使用调试工具时)。
当 "type" 包含相对 URI 时, 它将根据 [URI] 第 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 方案 [TAG] 可用于唯一标识问题类型:
tag:[email protected],2021-09-17:OutOfLuck
但是, 本规范鼓励使用可解析的 type URI, 因为将来可能需要解析该 URI。例如, 如果 API 设计者使用了上述 URI, 后来采用了一个解析 type URI 以发现有关错误信息的工具, 利用该功能将需要切换到可解析的 URI, 从而为问题类型创建新的标识并引入破坏性更改。
3.1.2. "status"
"status" 成员是一个 JSON 数字, 指示原始服务器为此问题发生生成的 HTTP 状态码 ([HTTP] 的第 15 节)。
"status" 成员 (如果存在) 仅供参考; 它传达用于消费者方便的 HTTP 状态码。生成器必须 (MUST) 在实际 HTTP 响应中使用相同的状态码, 以确保不理解此格式的通用 HTTP 软件仍能正确运行。有关其使用的进一步注意事项, 请参见第 5 节。
消费者可以使用 status 成员来确定生成器在状态码被更改时 (例如, 被中间件或缓存更改) 以及当消息内容在没有 HTTP 信息的情况下持久化时所使用的原始状态码。通用 HTTP 软件仍将使用 HTTP 状态码。
3.1.3. "title"
"title" 成员是一个包含问题类型的简短、人类可读摘要的 JSON 字符串。
除了用于本地化目的 (例如, 使用主动内容协商; 参见 [HTTP] 的第 12.1 节) 外, 它不应该 (SHOULD NOT) 在问题的不同发生之间发生变化。
"title" 字符串是建议性的, 仅包含在内供不了解 URI 语义且无法发现它们的用户使用 (例如, 在离线日志分析期间)。
3.1.4. "detail"
"detail" 成员是一个包含特定于此问题发生的人类可读解释的 JSON 字符串。
"detail" 字符串 (如果存在) 应该专注于帮助客户端纠正问题, 而不是提供调试信息。
消费者不应该 (SHOULD NOT) 解析 "detail" 成员以获取信息; 扩展是获取此类信息的更合适且更不易出错的方式。
3.1.5. "instance"
"instance" 成员是一个包含 URI 引用的 JSON 字符串, 用于标识问题的特定发生。
当 "instance" URI 可解引用时, 可以从中获取问题详情对象。它还可能通过使用主动内容协商 (参见 [HTTP] 的第 12.5.1 节) 以其他格式返回有关问题发生的信息。
当 "instance" URI 不可解引用时, 它用作问题发生的唯一标识符, 该标识符可能对服务器具有意义, 但对客户端不透明。
当 "instance" 包含相对 URI 时, 它将根据 [URI] 第 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) 使用特定于该问题类型的附加成员扩展问题详情对象。
例如, 我们上面的 out-of-credit 问题定义了两个这样的扩展 -- "balance" 和 "accounts", 用于传达附加的、特定于问题的信息。
类似地, "validation error" 示例定义了一个 "errors" 扩展, 其中包含找到的各个错误发生的列表, 以及每个错误的详情和位置指针。
使用问题详情的客户端必须 (MUST) 忽略它们不认识的任何此类扩展; 这允许问题类型演化并在将来包含附加信息。
在创建扩展时, 问题类型作者应该仔细选择其名称。要在 XML 格式中使用 (参见附录 B), 它们需要符合 [XML] 第 2.3 节中的 Name 规则。