1. 概要

RESTは、クライアントがサーバー上のリソースにアクセスして操作できるステートレスアーキテクチャです。 通常、RESTサービスはHTTPを利用して、管理する一連のリソースをアドバタイズし、クライアントがこれらのリソースの状態を取得または変更できるようにするAPIを提供します。

このチュートリアルでは、REST APIエラーを処理するためのいくつかのベストプラクティスについて学習します。これには、関連情報をユーザーに提供するための便利なアプローチ、大規模なWebサイトの例、SpringRESTアプリケーションの例を使用した具体的な実装が含まれます。

2. HTTPステータスコード

クライアントがHTTPサーバーにリクエストを送信し、サーバーがリクエストを正常に受信した場合、サーバーは、リクエストが正常に処理されたかどうかをクライアントに通知する必要があります。

HTTPは、ステータスコードの5つのカテゴリでこれを実現します。

  • 100レベル(情報)–サーバーはリクエストを確認します
  • 200レベル(成功)–サーバーは期待どおりにリクエストを完了しました
  • 300レベル(リダイレクト)–クライアントは、リクエストを完了するためにさらにアクションを実行する必要があります
  • 400レベル(クライアントエラー)–クライアントが無効な要求を送信しました
  • 500レベル(サーバーエラー)–サーバーのエラーが原因で、サーバーが有効な要求を実行できませんでした

応答コードに基づいて、クライアントは特定の要求の結果を推測できます。

3. エラーの処理

エラーを処理する最初のステップは、クライアントに適切なステータスコードを提供することです。 さらに、応答本文でより多くの情報を提供する必要がある場合があります。

3.1. 基本的な対応

エラーを処理する最も簡単な方法は、適切なステータスコードで応答することです。

一般的な応答コードは次のとおりです。

  • 400不正なリクエスト–クライアントが、必要なリクエストの本文やパラメータが不足しているなど、無効なリクエストを送信しました
  • 401 Unauthorized –クライアントはサーバーでの認証に失敗しました
  • 403 Forbidden –クライアントは認証されましたが、要求されたリソースにアクセスする権限がありません
  • 404 Not Found –要求されたリソースが存在しません
  • 412前提条件が失敗しました–リクエストヘッダーフィールドの1つ以上の条件がfalseと評価されました
  • 500内部サーバーエラー–サーバーで一般的なエラーが発生しました
  • 503 Service Unavailable –要求されたサービスは利用できません

基本的に、これらのコードにより、クライアントは発生したエラーの幅広い性質を理解できます。 たとえば、403エラーを受け取った場合、リクエストしたリソースにアクセスするための権限が不足していることがわかっています。 ただし、多くの場合、回答で補足の詳細を提供する必要があります。

500エラーは、リクエストの処理中にサーバーでいくつかの問題または例外が発生したことを示します。 一般的に、この内部エラーはクライアントの仕事ではありません。

したがって、クライアントへのこの種の応答を最小限に抑えるために、内部エラーの処理またはキャッチを熱心に試み、可能な限り他の適切なステータスコードで応答する必要があります。

たとえば、要求されたリソースが存在しないために例外が発生した場合、これを500エラーではなく404エラーとして公開する必要があります。

これは、500が返されるべきではないということではなく、サーバーが要求を実行できないような予期しない状況(サービスの停止など)に使用されるべきであるということだけです。

3.2. デフォルトのSpringエラー応答

これらの原則は非常に普及しているため、Springはデフォルトのエラー処理メカニズムでそれらを体系化しています。

実例を示すために、本を管理する単純なSpring RESTアプリケーションがあり、そのIDで本を取得するエンドポイントがあるとします。

curl -X GET -H "Accept: application/json" http://localhost:8082/spring-rest/api/book/1

IDが1の本がない場合、コントローラーはBookNotFoundExceptionをスローすると予想されます。

このエンドポイントでGETを実行すると、この例外がスローされたことがわかります。これが応答本文です。

{
    "timestamp":"2019-09-16T22:14:45.624+0000",
    "status":500,
    "error":"Internal Server Error",
    "message":"No message available",
    "path":"/api/book/1"
}

このデフォルトのエラーハンドラには、エラーが発生したときのタイムスタンプ、HTTPステータスコード、タイトル( error フィールド)、メッセージがデフォルトのエラーで有効になっている場合のメッセージ(デフォルトでは空白)が含まれていることに注意してください。 )、およびエラーが発生したURLパス。

これらのフィールドは、クライアントまたは開発者に問題のトラブルシューティングに役立つ情報を提供します。また、標準のエラー処理メカニズムを構成するいくつかのフィールドを構成します。

また、 BookNotFoundException がスローされると、SpringはHTTPステータスコード500を自動的に返すことに注意してください。 一部のAPIは500ステータスコードまたはその他の一般的なものを返しますが、FacebookおよびTwitter APIで見られるように、簡単にするためにすべてのエラーについて、可能な場合は最も具体的なエラーコードを使用するのが最善です。

この例では、 @ControllerAdvice を追加して、 BookNotFoundException がスローされたときに、APIがステータス404を返し、 NotFoundではなく500内部サーバーエラー

3.3. より詳細な回答

上記のSpringの例に見られるように、エラーの詳細を示すのにステータスコードでは不十分な場合があります。 必要に応じて、応答の本文を使用して、クライアントに追加情報を提供できます。

詳細な回答を提供する場合は、以下を含める必要があります。

  • エラー–エラーの一意の識別子
  • メッセージ–人間が読める簡単なメッセージ
  • 詳細–エラーのより長い説明

たとえば、クライアントが誤った資格情報を使用してリクエストを送信した場合、次の本文を使用して401応答を送信できます。

{
    "error": "auth-0001",
    "message": "Incorrect username and password",
    "detail": "Ensure that the username and password included in the request are correct"
}

エラーフィールドは応答コードと一致してはなりません。代わりに、アプリケーションに固有のエラーコードである必要があります。 一般に、 error フィールドには規則がなく、一意であることが期待されます。

通常、このフィールドには、英数字と、ダッシュやアンダースコアなどの接続文字のみが含まれます。 たとえば、 0001 auth-0001 、および incorrect-user-pass は、エラーコードの標準的な例です。

本文のメッセージ部分は通常、ユーザーインターフェイスで表示可能と見なされます。 したがって、サポートする場合は、このタイトルを翻訳する必要があります国際化 。 したがって、クライアントがフランス語に対応する Accept-Language ヘッダーを使用してリクエストを送信する場合、titleの値をフランス語に変換する必要があります。

詳細部分は、エンドユーザーではなく、クライアントの開発者が使用することを目的としているため、翻訳は必要ありません。

さらに、 help フィールドなど、クライアントがより多くの情報を見つけるためにたどることができるURLを提供することもできます。

{
    "error": "auth-0001",
    "message": "Incorrect username and password",
    "detail": "Ensure that the username and password included in the request are correct",
    "help": "https://example.com/help/error/auth-0001"
}

リクエストに対して複数のエラーを報告したい場合があります。

この場合、エラーをリストで返す必要があります。

{
    "errors": [
        {
            "error": "auth-0001",
            "message": "Incorrect username and password",
            "detail": "Ensure that the username and password included in the request are correct",
            "help": "https://example.com/help/error/auth-0001"
        },
        ...
    ]
}

そして、単一のエラーが発生すると、1つの要素を含むリストで応答します。

複数のエラーで応答することは、単純なアプリケーションには複雑すぎる可能性があることに注意してください。 多くの場合、最初のエラーまたは最も重大なエラーで応答するだけで十分です。

3.4. 標準化された応答ボディ

ほとんどのRESTAPIは同様の規則に従いますが、フィールドの名前や応答本文に含まれる情報など、詳細は通常異なります。 これらの違いにより、ライブラリとフレームワークがエラーを均一に処理することが困難になります。

REST APIエラー処理を標準化するために、IETFはRFC7807を考案しました。これにより、一般化されたエラー処理スキーマが作成されます。

このスキーマは、次の5つの部分で構成されています。

  1. type –エラーを分類するURI識別子
  2. title –エラーに関する簡単で人間が読めるメッセージ
  3. status – HTTP応答コード(オプション)
  4. detail –人間が読める形式のエラーの説明
  5. instance –エラーの特定の発生を識別するURI

カスタムエラー応答本文を使用する代わりに、本文を変換できます。

{
    "type": "/errors/incorrect-user-pass",
    "title": "Incorrect username or password.",
    "status": 401,
    "detail": "Authentication failed due to incorrect username or password.",
    "instance": "/login/log/abc123"
}

type フィールドはエラーのタイプを分類し、instanceはそれぞれクラスおよびオブジェクトと同様の方法でエラーの特定の発生を識別することに注意してください。

URIを使用することで、クライアントはこれらのパスをたどって、HATEOASリンクを使用してRESTAPIをナビゲートできるのと同じ方法で、エラーに関する詳細情報を見つけることができます。

RFC 7807への準拠はオプションですが、均一性が必要な場合は有利です。

4. 例

上記の方法は、最も一般的なRESTAPIのいくつかで一般的です。 フィールドやフォーマットの具体的な名前はサイトによって異なる場合がありますが、一般的なパターンはほぼ普遍的です。

4.1. ツイッター

必要な認証データを提供せずにGETリクエストを送信しましょう。

curl -X GET https://api.twitter.com/1.1/statuses/update.json?include_entities=true

Twitter APIは、次の本文でエラーで応答します。

{
    "errors": [
        {
            "code":215,
            "message":"Bad Authentication data."
        }
    ]
}

この応答には、エラーコードとメッセージを含む単一のエラーを含むリストが含まれています。 Twitterの場合、詳細なメッセージは表示されず、認証が失敗したことを示すために、より具体的な401エラーではなく、一般的なエラーが使用されます。

以下のSpringの例でわかるように、より一般的なステータスコードを実装する方が簡単な場合があります。 これにより、開発者は例外のグループをキャッチし、返される必要のあるステータスコードを区別できなくなります。 ただし、可能であれば、最も具体的なステータスコードを使用する必要があります。

4.2. フェイスブック

Twitterと同様に、FacebookのGraphREST API も、応答に詳細情報を含めます。

FacebookGraphAPIで認証するためのPOSTリクエストを実行してみましょう。

curl -X GET https://graph.facebook.com/oauth/access_token?client_id=foo&client_secret=bar&grant_type=baz

このエラーが発生します:

{
    "error": {
        "message": "Missing redirect_uri parameter.",
        "type": "OAuthException",
        "code": 191,
        "fbtrace_id": "AWswcVwbcqfgrSgjG80MtqJ"
    }
}

Twitterと同様に、Facebookも、より具体的な400レベルのエラーではなく、一般的なエラーを使用して失敗を示します。 Facebookには、メッセージと数値コードに加えて、エラーを分類するタイプフィールドと、内部サポート識別子として機能するトレースID( fbtrace_id )も含まれています。 ]。

5. 結論

この記事では、RESTAPIエラー処理のベストプラクティスのいくつかを検討しました。

  • 特定のステータスコードを提供する
  • 応答機関に追加情報を含める
  • 例外を一律に処理する

エラー処理の詳細はアプリケーションによって異なりますが、 これらの一般原則は、ほぼすべてのREST APIに適用され、可能な場合は順守する必要があります。

これにより、クライアントは一貫した方法でエラーを処理できるだけでなく、RESTAPIを実装するときに作成するコードも簡素化されます。

この記事で参照されているコードは、GitHubから入手できます。