REST APIの例外設計
REST APIを設計する場合に、エラーをどのステータスコードで返却するか議論になることがあります。例えば、以下のような場合が挙げられます。
- キー指定のリクエストでDBにデータがない場合(例:
GET /books/1
) - 一覧のリクエストでDBにデータがない場合(例:
GET /books
) - 必須項目がない、型が合わないといった場合(例:
GET /books/find?count=bar
) - ビジネスルールに違反する場合(例:
POST /purchase
) - 実行時エラー(例:
NullPointerException
)
クライアントが適切にエラーを処理できるように、レスポンスにエラー原因を入れることが一般的です。では、ステータスコードは何がいいのでしょうか。HTTPのステータスコードはRFCで定義されているし、RESTの考え方はWebや書籍にまとまっていますが、あくまでも考え方なので人によって意見が分かれる場合があります。
2つの論点を挙げて考えてみましょう。
論点1. レイヤの責務と障害の切り分け
Spring Bootのアプリケーションを例に考えてみましょう。クライアントから見ると以下のレイヤがあります。
- クライアント(例:XHR、OkHttp)
- ロードバランサ(例:ELB、Nginx)
- アプリケーションサーバ(例:Tomcat)
- フレームワーク(例:Spring MVC)
- アプリケーション層(Controller)
- ドメイン層(Service、Repository)
キー指定のリクエストでDBにデータがない場合、404を返す設計が一般的です。しかし、アプリケーションサーバやフレームワークなどのインフラ層も404を返す場合があるため、区別できるようにアプリケーション層は一律で200を返した方がよいという意見もあるでしょう。何か障害が発生した場合に、インフラ層の設定が悪いのか、リクエストのURLが間違っているのか、DBにデータがないだけで想定通りなのか、ステータスコードだけでは切り分けができません。
入力チェックで400を返す、実行時エラーで500を返すといった場合も同じことが言えますね。
これに対しては、エラー原因をレスポンスヘッダやレスポンス本体に含めることで、切り分けが可能になります。APIクライアントはレスポンス本体から原因を特定して、適切なUXを実装します。システム監視では、ステータスコードとレスポンスヘッダから原因を特定して、適切な監視条件を設定します。レスポンスが200である必要はないでしょう。
論点2. 宣言的なAPIクライアント
エラーの場合に200を返す設計とした場合、APIクライアントはレスポンスの内容から実行時に型を判断する必要が出てきます。例えば、 GET /books/1
のレスポンスを Book
クラスに入れるのか Error
クラスに入れるのか、実行時に判断する必要が生じます。
Feignなどの宣言的なAPIクライアントは、URLに対してレスポンスの型が一意に決まるように設計されています。実行時に型が決まるような設計とは相性が悪いといえます。詳しくは調べていないのですが、200でエラーレスポンスを処理するには特別な実装が必要になると思われます。
結論
リソースの内容を返す場合は200、エラーの場合は404や500などを返す設計でよさそうです。