おかえりなさい!Problem Details標準を使って、APIエラー処理の世界に飛び込みましょう。 パートIでは、一貫性のないエラー報告から生じるいくつかの課題について説明しました。これらの課題は、APIの生産が増加するにつれてデジタルエコシステムが拡大するにつれて増幅されます。実行可能なエラーメッセージの提供の重要性から、カスタムエラーメソッドやセキュリティ脆弱性の落とし穴まで、RFC 9457のProblem DetailsがAPIエラー応答を標準化するための堅牢なフレームワークをどのように提供するかを見てきました。
RFC 7807からRFC 9457への進化について強調し、APIエラーの明確化における重要な進歩を含め、エラー処理を開発者の悪夢から合理化された、情報豊富な、実行可能なプロセスへと変えました。この標準は、開発者にとってエラー処理をより直感的にするだけでなく、デジタルプラットフォーム全体のセキュリティと一貫性を向上させます。
さて、標準の利点とフレームワークを十分に理解したところで、実践的な実装に移行しましょう。このセグメントでは、実際の例とリソースを使用して、システムが可能な限り最善の方法で「悪い知らせ」を配信できるように、API内でProblem Detailsを活用する方法を案内します。
Problem Detailsの開始
チームまたは組織に新しい標準を導入する際、開始が最も難しいステップとなることがよくあります。まず、RFC 9457 [1]に慣れてください。
ゼロから始めない
よくある落とし穴を避け、Problem Detailsの採用を加速するために、以下のリソースと成果物を活用してください。これにより、採用をブートストラップし、チームと一貫したエラー処理を提供するための道を切り開くことができます。
レジストリ
シリーズのパートIで述べたように、新しいRFC 9457は、一般的な問題タイプのURIのレジストリの概念を導入しています。IANA [2]でホストされている公式レジストリとSmartBear Problems Registry [3]は、APIに関連する問題タイプを決定する際に貴重なリソースとして機能します。
- IANAレジストリ: 公式レジストリには、すぐに使用できる、または独自の問題タイプを定義する際の参照として使用できる標準化された問題タイプURIが保持されています。
- SmartBear Problems Registry: このレジストリは、SmartBearチームがキュレーションした、さまざまなAPIシナリオに固有の一般的な問題タイプのカタログを提供しています。将来的には、一部がIANAレジストリに移行する可能性があります。
オブジェクト、スキーマ、拡張性
標準の利点は、エラー詳細の形状に関連する多くの議論を回避できることです。RFCは、以下の非規範的なHTTP Problem Details用のJSONスキーマを提供しており、エラーの基本形状を保証します。
上記のJSONスキーマでカバーされる以上の情報を提供する必要がある状況では、組み込みの拡張性が強力なメカニズムであり、チームのニーズに合わせて標準を調整できるので安心してください。
拡張性には独自の課題があります。したがって、拡張ポイントを明確に定義し、Problem Detailsを利用するクライアントに、認識しない拡張を無視しなければならないことを伝えるのが良い習慣です。実装者(および応答を消費する者)にとってプロセスを予測可能にするために、拡張を含むJSONスキーマを作成することも推奨します。
以下は、SmartBearの新しいAPI内で問題の詳細に使用している`errors`および`code`拡張を含むJSONスキーマ [5]です。
{
"$schema": "https://json-schema.dokyumento.jp/draft/2019-09/schema",
"type": "object",
"properties": {
"type": {
"type": "string",
"description": "A URI reference that identifies the problem type.",
"format": "uri",
"maxLength": 1024
},
"status": {
"type": "integer",
"description": "The HTTP status code generated by the origin server for this occurrence of the problem.",
"format": "int32",
"minimum": 100,
"maximum": 599
},
"title": {
"type": "string",
"description": "A short, human-readable summary of the problem type. It should not change from occurrence to occurrence of the problem, except for purposes of localization.",
"maxLength": 1024
},
"detail": {
"type": "string",
"description": "A human-readable explanation specific to this occurrence of the problem.",
"maxLength": 4096
},
"instance": {
"type": "string",
"description": "A URI reference that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced.",
"maxLength": 1024
},
"code": {
"type": "string",
"description": "An API specific error code aiding the provider team understand the error based on their own potential taxonomy or registry.",
"maxLength": 50
},
"errors": {
"type": "array",
"description": "An array of error details to accompany a problem details response.",
"maxItems": 1000,
"items": {
"type": "object",
"description": "An object to provide explicit details on a problem towards an API consumer.",
"properties": {
"detail": {
"type": "string",
"description": "A granular description on the specific error related to a body property, query parameter, path parameters, and/or header.",
"maxLength": 4096
},
"pointer": {
"type": "string",
"description": "A JSON Pointer to a specific request body property that is the source of error.",
"maxLength": 1024
},
"parameter": {
"type": "string",
"description": "The name of the query or path parameter that is the source of error.",
"maxLength": 1024
},
"header": {
"type": "string",
"description": "The name of the header that is the source of error.",
"maxLength": 1024
},
"code": {
"type": "string",
"description": "A string containing additional provider specific codes to identify the error context.",
"maxLength": 50
}
},
"required": [
"detail"
]
}
}
},
"required": [
"detail"
]
}
これにより、パラメーターまたはリクエスト本文に関連するエラーの発生を記述する強力かつ詳細な機能が提供されます。
上記のJSONスキーマに基づいたいくつかの例を見てみましょう
- 欠落しているリクエストパラメーター(クエリパラメーターなど)の問題については、`errors`拡張を利用して、`details`および`parameter`プロパティを介して欠落しているパラメーターに関する明示的な情報を提供できます。
{
"type": "https://problems-registry.smartbear.com/missing-request-parameter",
"status": 400,
"title": "Missing request parameter",
"detail": "The request is missing an expected query or path parameter.",
"code": "400-03",
"errors": [
{
"detail": "The query parameter {name} is required.",
"parameter": "name"
}
]
}
- 不正な形式のリクエストボディプロパティの問題については、`errors`拡張を利用して、問題に関する明示的な情報と、`details`および`pointer`(プロパティの場所へのJSONポインターを指定)プロパティを介したプロパティの場所を提供できます。
{
"type": "https://problems-registry.smartbear.com/invalid-body-property-format",
"status": 400,
"title": "Invalid Body Property Format",
"detail": "The request body contains a malformed property.",
"code": "400-04",
"errors": [
{
"detail": "Must be a positive integer",
"pointer": "/quantity"
}
]
}
- 複数のエラーが見つかり、過剰なチャット的なエンゲージメントを強制するのではなく、すべての違反をクライアントに返したい場合は、`errors`配列拡張を利用して、関連する問題タイプに適用されるすべてのエラーの詳細を含めることができます。
{
"type": "https://problems-registry.smartbear.com/business-rule-violation",
"status": 422,
"title": "Business Rule Violation",
"detail": "The request body is invalid and not meeting business rules.",
"code": "422-01",
"errors": [
{
"detail": "Maximum quantity allowed in 999",
"pointer": "/quantity"
},
{
"detail": "We do not offer `next-day` delivery to non-EU addresses",
"pointer": "/shippingAddress/country"
},
{
"detail": "We do not offer `next-day` delivery to non-EU addresses",
"pointer": "/shippingOption"
}
]
}
OpenAPIで既製のドメインで加速する
次のAPIプロジェクトに問題の詳細をさらに簡単に導入するために、上記の資産をすぐに使用できるSwaggerHubドメイン [4]にまとめました。これは、OpenAPI記述のさまざまな部分から参照できます。
ドメインは以下で構成されています
- スキーマ: HTTP Problem Detailsの完全版および拡張版(別名「オピニオン付き」)スキーマ

- 例: サポートされている問題タイプに対する代表的な応答例の豊富なリスト

- 応答: サポートされている問題タイプに対するOpenAPI互換応答の参照可能なリスト

以下のセクションでは、OpenAPI記述内で無料の公開ドメインを活用する例を説明します。
OpenAPIで問題の詳細を使用する
OpenAPIの記述でProblem Detailsを活用することは、思っているよりも簡単です。さらに、上記の成果物の一部を利用することで、さらに簡単になります。Bookstore APIの簡単なOpenAPI記述を作成して、API設計におけるエラー応答を改善できることを示しましょう。
OpenAPI記述の最初のパスでは、基本オブジェクト(情報、タグ、サーバー)、書籍を取得し注文を出すための2つのリソース、および書籍と注文リソースに関連するスキーマを設定します。
openapi: 3.0.3
info:
title: Bookstore API
version: 0.0.1
description: |
The **Books API** - allows searching of books from the book catalog as well as retrieving the specific details on a selected book. Once you find the book you are looking for, you can make an order.
termsOfService: https://swagger.dokyumento.jp/terms/
contact:
name: DevRel at SmartBear
email: [email protected]
license:
name: Apache 2.0
url: https://apache.dokyumento.jp/licenses/LICENSE-2.0.html
tags:
- name: Bookstore
description: APIs for our fictional bookstore
servers:
# Added by API Auto Mocking Plugin
- description: SwaggerHub API Auto Mocking
url: https://virtserver.swaggerhub.com/frank-kilcommins/Bookstore-API/1.0.0
paths:
/books:
get:
summary: Get a list of books based on the provided criteria
description: |
This API method supports searching the book catalog based on book title or author name
operationId: getBooks
tags:
- Bookstore
parameters:
- name: title
description: The title (or partial title) of a book
in: query
required: false
schema:
type: string
maxLength: 200
format: string
- name: author
description: The author’s name (or partial author name)
in: query
required: false
schema:
type: string
maxLength: 150
format: string
- name: limit
description: The maximum number of books to return
in: query
required: false
schema:
type: integer
format: int64
minimum: 1
maximum: 1000
default: 10
responses:
'200':
$ref: '#/components/responses/books'
'400':
description: 400 Bad Request
'401':
description: 401 Unauthorized
'500':
description: 500 Internal Server Error
content:
application/json:
schema:
type: object
properties:
code:
type: integer
format: int32
example: 500
message:
type: string
example: "Internal Server Error"
details:
type: string
example: "An unexpected error occurred"
/orders:
post:
summary: Place book order
description: |
This API method allows placing an order for one or more books
operationId: createOrder
tags:
- Bookstore
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Order'
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/OrderDetails'
'401':
description: 401 Unauthorized
'422':
description: Validation Error
'500':
description: Internal Server Error
components:
schemas:
BookOrder:
type: object
properties:
bookId:
type: string
description: The book identifier
format: uuid
maxLength: 36
example: 87da4501-4b52-4ea2-a2be-7dda8650f7eb
quantity:
type: integer
format: int64
minimum: 1
maximum: 10000
Order:
properties:
books:
type: array
maxItems: 100
items:
$ref: '#/components/schemas/BookOrder'
deliveryAddress:
type: string
minLength: 10
maxLength: 500
type: object
required:
- books
- deliveryAddress
additionalProperties: false
OrderDetails:
properties:
books:
type: array
description: The books that are part of the order
maxItems: 1000
items:
$ref: '#/components/schemas/BookOrder'
deliveryAddress:
type: string
description: The address to deliver the order to
maxLength: 1000
id:
type: string
description: The order identifier
format: uuid
maxLength: 36
example: 87da4501-4b52-4ea2-a2be-7dda8650f7eb
createdAt:
type: string
description: When the order was created
format: date-time
pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}T[012][0-9]:[0-5][0-9]:[0-5][0-9]Z$'
maxLength: 250
updatedAt:
type: string
description: When the order was updated
format: date-time
pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}T[012][0-9]:[0-5][0-9]:[0-5][0-9]Z$'
maxLength: 250
status:
$ref: '#/components/schemas/OrderStatusEnum'
type: object
required:
- books
- deliveryAddress
- id
- createdAt
- updatedAt
OrderStatusEnum:
type: string
enum:
- placed
- paid
- delivered
Book:
description: The schema object for a Book
type: object
additionalProperties: false
properties:
id:
description: the identifier for a book
type: string
format: uuid
maxLength: 36
example: 87da4501-4b52-4ea2-a2be-7dda8650f7eb
title:
type: string
description: The book title
maxLength: 1000
example: "Designing APIs with Swagger and OpenAPI"
authors:
type: array
description: A list of book authors
maxItems: 1000
items:
type: string
description: A string containing an author's name
maxLength: 250
minItems: 1
maxItems: 1000
example: "[Joshua S. Ponelat, Lukas L. Rosenstock]"
published:
type: string
format: date
pattern: "^[0-9]{4}-[0-9]{2}-[0-9]{2}$"
maxLength: 250
example: "2022-05-01"
responses:
books:
description: List of books
content:
application/json:
schema:
type: array
maxItems: 1000
items:
$ref: '#/components/schemas/Book'
securitySchemes:
ApiKeyAuth:
type: apiKey
in: header
name: api_key
security:
- ApiKeyAuth: []
この簡単なOpenAPI記述により、複数のエラー応答をカバーしているため堅牢だと考えられるインタラクティブなAPIドキュメントが生成されます。しかし、APIで発生する可能性のあるエラーを浮上させるのに役立つ重要な要素が不足しています。

一貫性のないエラーをどうすれば検出できますか?
API内でHTTP Problem Details標準を適用し忘れないように、以下のSpectralルールをガバナンススタイルガイドに追加することをお勧めします。これらの2つのルールは、コンテンツなしでエラーを定義した場合や、エラー詳細を提供するのに予期しない形式を使用した場合にフィードバックを提供します。
# Author: Frank Kilcommins (https://github.com/frankkilcommins)
no-errors-without-content:
message: Error responses MUST describe the error
description: Error responses should describe the error that occurred. This is useful for the API consumer to understand what went wrong and how to fix it. Please provide a description of the error in the response.
given: $.paths[*]..responses[?(@property.match(/^(4|5)/))]
then:
field: content
function: truthy
formats: [oas3]
severity: warn
# Author: Phil Sturgeon (https://github.com/philsturgeon)
no-unknown-error-format:
message: Error response should use a standard error format.
description: Error responses can be unique snowflakes, different to every API, but standards exist to make them consistent, which reduces surprises and increase interoperability. Please use either RFC 7807 (https://tools.ietf.org/html/rfc7807) or the JSON:API Error format (https://jsonapi.dokyumento.jp/format/#error-objects).
given: $.paths[*]..responses[?(@property.match(/^(4|5)/))].content.*~
then:
function: enumeration
functionOptions:
values:
- application/vnd.api+json
- application/problem+json
- application/problem+xml
formats: [oas3]
severity: warn
これらのルールが適用されると、Bookstore APIの`0.0.1`バージョンについて、以下の具体的なフィードバックが得られます。
63:15 warning no-errors-without-content Error responses MUST describe the error paths./books.get.responses[400]
65:15 warning no-errors-without-content Error responses MUST describe the error paths./books.get.responses[401]
70:30 warning no-unknown-error-format Error response should use a standard error format. paths./books.get.responses[500].content.application/json
105:15 warning no-errors-without-content Error responses MUST describe the error paths./orders.post.responses[401]
107:15 warning no-errors-without-content Error responses MUST describe the error paths./orders.post.responses[422]
109:15 warning no-errors-without-content Error responses MUST describe the error paths./orders.post.responses[500]
エラー応答を改善するにはどうすればよいですか?
初期設計における欠点がどこにあるかを把握できたので、エラー応答をどのように改善できるでしょうか?SwaggerHub Problem Detailsドメインは公開されているため、Bookstore APIのエラー応答を簡単に改善するために利用できます。
多くの場合、直接の応答と例は構造を一般的に表しているため、それらをそのまま活用できます。これは`POST /orders`リソースで行うことです。
/orders:
post:
summary: Place book order
description: |
This API method allows placing an order for one or more books
operationId: createOrder
tags:
- Bookstore
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Order'
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/OrderDetails'
'400':
$ref: 'https://api.swaggerhub.com/domains/smartbear-public/ProblemDetails/1.0.0#/components/responses/BadRequest'
'401':
$ref: 'https://api.swaggerhub.com/domains/smartbear-public/ProblemDetails/1.0.0#/components/responses/Unauthorized'
'422':
$ref: 'https://api.swaggerhub.com/domains/smartbear-public/ProblemDetails/1.0.0#/components/responses/ValidationError'
'500':
$ref: 'https://api.swaggerhub.com/domains/smartbear-public/ProblemDetails/1.0.0#/components/responses/ServerError'
'503':
$ref: 'https://api.swaggerhub.com/domains/smartbear-public/ProblemDetails/1.0.0#/components/responses/ServiceUnavailable'
これにより、エラーのより豊かで明示的な表現が得られます。

他の状況では、おそらく特定のエラー例のみが適用されるため(または独自の例を作成したい場合)、エラー応答を調整したい場合があります。これは、公開されているスキーマを活用しながらも可能であり、リクエストボディに関連する例は適用されないため、`GET /books`パスに必要です。以下に、再利用可能なドメインによって公開されているスキーマを活用しながら、応答のコンテンツとエンコーディングを設定します。また、パスに適用される例も明示的に参照します。
responses:
'200':
$ref: '#/components/responses/books'
'400':
description: |
The request was malformed or could not be processed.
Examples of `Bad Request` problem detail responses:
- [Missing request parameter](https://problems-registry.smartbear.com/missing-request-parameter/)
- [Invalid request parameter format](https://problems-registry.smartbear.com/invalid-request-parameter-format/)
- [Invalid request parameter value](https://problems-registry.smartbear.com/invalid-request-parameter-value/)
content:
application/problem+json:
schema:
$ref: 'https://api.swaggerhub.com/domains/smartbear-public/ProblemDetails/1.0.0#/components/schemas/ProblemDetails'
examples:
missingRequestParameterWithErrors:
$ref: 'https://api.swaggerhub.com/domains/smartbear-public/ProblemDetails/1.0.0#/components/examples/missing-request-parameter-with-errors'
invalidRequestParameterFormatWithErrors:
$ref: 'https://api.swaggerhub.com/domains/smartbear-public/ProblemDetails/1.0.0#/components/examples/invalid-request-parameter-format-with-errors'
invalidRequestParameterValueWithErrors:
$ref: 'https://api.swaggerhub.com/domains/smartbear-public/ProblemDetails/1.0.0#/components/examples/invalid-request-parameter-value-with-errors'
'401':
$ref: 'https://api.swaggerhub.com/domains/smartbear-public/ProblemDetails/1.0.0#/components/responses/Unauthorized'
'500':
$ref: 'https://api.swaggerhub.com/domains/smartbear-public/ProblemDetails/1.0.0#/components/responses/ServerError'
'503':
$ref: 'https://api.swaggerhub.com/domains/smartbear-public/ProblemDetails/1.0.0#/components/responses/ServiceUnavailable'
改善されたBookstore APIは、SwaggerHub [7]で直接確認できます。
実際の例
多くのAPIプロバイダー、ツールベンダー、およびプログラミングフレームワークによる標準の採用は心強いです。
以下に、すでに標準を採用しているSmartBear APIのいくつかを示します。
結論
提供されたリソースと例により、APIでProblem Detailsの実装を開始する準備が整っています。公開されているドメインとレジストリを活用して、作業を加速し、初期費用を削減し、チームが車輪を再発明する必要がないようにすることをためらわないでください。さらに重要なことは、これらのツールがAPI環境全体で一貫性を維持し、エンドユーザーと開発者の両方にとってエラー処理エクスペリエンスを向上させるのに役立つことです。