👋

Web API のレスポンスについて考える

2022/03/20に公開

API のレスポンスの正解って何なの?

API を作るたびにレスポンスはどうすればいいのかしらと迷い。
毎回悩むので自分用にちょっとまとめてみようと思いまして。

注意: この記事は個人の感想なので、ベストプラクティスではありません。

デファクトスタンダードとか、ベストプラクティスとかはあるの?

現在はこれかなというのは次の2点。

  1. 適切なステータスコードをつけましょう
  2. エラーの場合は RFC7807 っていうのがある

RFC 7807 - Problem Details for HTTP APIs

HTTP APIの新しいエラー応答フォーマットを定義する必要をなくすために、HTTP応答でマシンが読み取り可能なエラーの詳細を伝える方法として「問題の詳細」を定義したもの。

問題の詳細のデータモデルはJSONオブジェクトで、「application / problem + json」メディアタイプを使用する。

問題詳細オブジェクトには、次のメンバーを含めることができる。

  • "type" (string) - 問題のタイプを識別するURI参照
  • "title" (string) - 人間が読める形式の問題タイプの概要
  • "status" (number) - この問題の発生に対してオリジンサーバーによって生成されたHTTPステータスコード
  • "detail" (string) - この問題の発生に固有の、人間が読める説明
  • "instance" (string) - 問題の特定の発生を識別するURI参照
  • (拡張メンバー) - 問題固有の追加情報

例) Laravel でこの形式のレスポンスを返す

return "the out-of-credit problem"
return response()->json([
    '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'
    ]
], 403, [
    'Content-Type' => 'application/problem+json',
    'Content-Language' => 'en'
]);

json メソッドの第2引数にステータスコードをセット、第3引数の headers に Content-Type をセットすると 'application/json' より優先(上書き?)される。

"type" は "逆参照すると、問題の種類について人間が読める形式のドキュメントが提供されるようになる" とのことなので、問題一覧みたいなページを作る必要がありそう。そこまでできるかなぁ?ない場合は "about:blank" とみなされるらしい。うーん、なしでもいいですか。

あと、例として挙げられているレスポンスの Content-Language が英語なのだけれど、日本人なわたしが作る日本人しか使わない API は Content-Language: ja にしても許されるのだろうか。だってほら、全部英語は厳しい気がする。

リクエストメソッドごとにレスポンスを考えてみる

GET

GETでリクエストするのはデータの取得。

  • 200 OK
    レスポンスボディでデータを返す

  • 404 Not Found
    該当データがみつからない

    例えばこんなかんじ?
    HTTP/1.1 404 Not Found
    Content-Type: application/problem+json
    Content-Language: en
    
    {
        "title": "Resource not found.",
        "detail": "The requested resource does not exist.",
        "instance": "/resource/404",
    }
    

POST

POSTでリクエストするのはデータの新規登録、もしくはリクエストに含まれるデータを利用した何らかの処理。

  • 200 OK
    レスポンスボディで処理結果を返す
  • 201 Created
    リクエストは成功し、その結果新たなリソースが作成された場合
    レスポンスボディで作成したリソースを返す
  • 400 Bad Request
    必須パラメータが不足しているなど、リクエストが無効な場合
  • 409 Conflict
    ユニークキーの重複など、何らかの理由でリソースの作成に失敗した場合

PUT | PATCH

PUT | PATCHでリクエストするのはデータの更新。

  • 200 OK
    レスポンスボディで更新したリソースを返す
  • 400 Bad Request
    必須パラメータが不足しているなど、リクエストが無効な場合
  • 404 Not Found
    更新対象のリソースが存在しない場合
  • 409 Conflict
    何らかの理由でリソースの更新に失敗した場合

DELETE

DELETEでリクエストするのはデータの削除。

  • 200 OK
    レスポンスボディは...IDとか?
  • 404 Not Found
    削除対象のリソースが存在しない場合
    (既にないというのを気にしない場合は返さなくてもいいのかも?)

メソッドに関係なく起こりそうなエラー

  • 400 Bad Request
    構文が無効であるためサーバーがリクエストを理解できない
  • 401 Unauthorized
    認証が必要
  • 403 Forbidden
    アクセス権がない
  • 404 Not Found
    URLが解釈できない

今回思ったこと

RFC 7807 にきっちり対応するのは厳しそうだけど、

  1. エラーのときにJSONを返すこと
  2. 適切なステータスコードを設定すること
  3. Content-Type は "application/problem+json"
  4. タイトルと詳細、エラー解決のために必要な情報(値)を含めること

くらいなら実践できるかな。

Discussion