Web API開発において、エラーをどういう形式で返却するか、というのは設計する際の悩みポイントかもしれません。エラーレスポンスの仕様の1つとして RFC7807 Problem Details for HTTP APIs があります。
この形式をサポートしつつ Rails や Sinatra でも使える Ruby ライブラリをこの度作りましたのでそのご紹介。その名も ProblemDetails.
本記事では Rails での使い方について。
ProblemDetailsの機能概要
gem では以下の機能をサポートしています。
- RFC7807形式を実装したオブジェクトクラス
- Railsサポート: problem detail 形式でレスポンスする problem renderer
- Sinatraサポート: problem render function
problem details 形式とは?
実際のレスポンスは以下のようになります。Content-Type: application/problem+json
というのが特徴的。レスポンスボディ自体はJSON形式です。type, title, detail, instance はRFCで規定された予約キー。追加で任意のキー(下の例ではbalance, accounts)も入れることは可能です。
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"]
}
仕様の詳細はRFCか、日本語の記事ではHTTP APIの詳細なエラー情報をレスポンスに持たせるための仕様が詳しいです。
Rails で problem detail 形式のエラーを返却する
インストール
problem_details-rails
gem をインストールしてください。
# Gemfile
gem 'problem_details-rails'
使い方
コントローラで render
メソッドに problem:
オプションを渡せるようになります。 problem:
の値は Hash にする必要があります。与えられた Hash に加え、RFCで規定された予約キーが自動的にマージされた値がエラーメッセージとしてレンダリングされます。
例えば以下の例ではバリデーションに失敗すると problem detail 形式でエラーレスポンスが返却されます。
# app/controllers/api/posts_controller.rb
class Api::PostsController < ApplicationController
def create
@post = Post.new(params[:post])
if @post.save
render json: @post
else
render problem: { errors: @post.errors }, status: :unprocessable_entity
end
end
end
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/problem+json; charset=utf-8
{
"type": "about:blank",
"title": "Unprocessable Entity",
"status": 422,
"errors": {
"body": [
"can't be blank"
]
}
}
gem を使う便利ポイントとしては、 明示的にproblem detailで返却することがアプリケーションのコード上に宣言できている点。また、problem:
に任意のエラーメッセージを入れるだけで problem detail 形式で適宜デフォルト値がセットされた形で返却される点です。以下の処理が自動的に行われます。
- Content-Typeがセットされる
- RFCで規定された予約キー status, type, title が自動セットされる
- HTTPのレスポンスコードと同じ値がボディにもセットされる
- type が自動的にセットされる(RFCで規定されているデフォルト値
abount:blank
が入る) - status コードから自動で title がセットされる
- RFCで予約されていないキー(例: errors) はトップレベルのキーとしてマージされる
なお、type, title 等の予約キーは problem: {...}
の中で明示的に指定することで上書くことは可能です。
まとめ
problem detail 形式をサポートするライブラリ ProblemDetails の Rails での使い方を紹介しました。
Happy API life!!
こう思う
problem details 形式の記事はサーバサイドのはあるけど、クライアントサイドの記事は見たことないなあ、、という感想で problem details を使ってどうクライアントサイドがレバレッジされてるか気になります。そしてその他のエラー形式を使っているなど、皆様どう運用されているのかな...? 気になることろです。