次の方法で共有


RESTful Web API 設計のベスト プラクティス

RESTful Web API の実装は、Representational State Transfer (REST) アーキテクチャの原則を使用して、クライアントとサービス間のステートレスで疎結合のインターフェイスを実現する Web API です。 RESTful である Web API は、リソースに対して操作を実行し、ハイパーメディア リンクと HTTP 操作状態コードを含むリソースの表現を返す標準 HTTP プロトコルをサポートしています。

RESTful Web API は、次の原則に従う必要があります。

  • プラットフォームに依存していません。つまり、クライアントは内部実装に関係なく Web API を呼び出すことができます。 プラットフォームの独立性を実現するために、Web API は標準プロトコルとして HTTP を使用し、明確なドキュメントを提供し、JSON や XML などの使い慣れたデータ交換形式をサポートします。

  • 疎結合は、クライアントと Web サービスが独立して進化できることを意味します。 クライアントは Web サービスの内部実装を知る必要はありません。また、Web サービスはクライアントの内部実装を知る必要はありません。 RESTful Web API で疎結合を実現するには、標準プロトコルのみを使用し、クライアントと Web サービスが交換するデータの形式に同意できるようにするメカニズムを実装します。

この記事では、RESTful Web API を設計するためのベスト プラクティスについて説明します。 また、理解しやすく、柔軟性があり、保守可能な Web API を構築するための一般的な設計パターンと考慮事項についても説明します。

RESTful Web API の設計概念

RESTful Web API を実装するには、次の概念を理解する必要があります。

  • Uniform Resource Identifier (URI): REST API は、クライアントがアクセスできる任意の種類のオブジェクト、データ、またはサービスであるリソースを中心に設計されています。 各リソースは、そのリソースを一意に識別する URI によって表されます。 たとえば、特定の顧客注文の URI は次のようになります。

    https://api.contoso.com/orders/1
    
  • リソース表現 は、URI によって識別されるリソースを、XML や JSON などの特定の形式で HTTP プロトコル経由でエンコードおよび転送する方法を定義します。 特定のリソースを取得するクライアントは、API への要求でリソースの URI を使用する必要があります。 API は、URI が示すデータのリソース表現を返します。 たとえば、クライアントは URI 識別子に対して GET 要求を行 https://api.contoso.com/orders/1 、次の JSON 本文を受信できます。

    {"orderId":1,"orderValue":99.9,"productId":1,"quantity":1}
    
  • 統一インターフェイス は、RESTful API がクライアントとサービスの実装間の疎結合を実現する方法です。 HTTP 上に構築された REST API の場合、uniform インターフェイスには、標準の HTTP 動詞を使用して、リソースに対する GETPOSTPUTPATCHDELETE などの操作を実行することが含まれます。

  • ステートレス要求モデル: RESTful API はステートレス要求モデルを使用します。つまり、HTTP 要求は独立しており、任意の順序で発生する可能性があります。 このため、要求間で一時的な状態情報を保持することはできません。 情報が格納される唯一の場所はリソース自体にあり、各要求はアトミック操作である必要があります。 ステートレス要求モデルは、クライアントと特定のサーバー間のアフィニティを保持する必要がないため、高いスケーラビリティをサポートします。 ただし、ステートレス モデルでは、Web サービスのバックエンド ストレージのスケーラビリティに関する課題があるため、スケーラビリティを制限することもできます。 データ ストアをスケールアウトする方法の詳細については、「データ のパーティション分割」を参照してください。

  • Hypermedia のリンク: REST API は、各リソース表現に含まれるハイパーメディア リンクによって駆動できます。 たとえば、次のコード ブロックは、注文の JSON 表現を示しています。 これには、注文に関連付けられている顧客を取得または更新するためのリンクが含まれています。

    {
      "orderID":3,
      "productID":2,
      "quantity":4,
      "orderValue":16.60,
      "links": [
        {"rel":"product","href":"https://api.contoso.com/customers/3", "action":"GET" },
        {"rel":"product","href":"https://api.contoso.com/customers/3", "action":"PUT" }
      ]
    }
    

RESTful Web API リソース URI の定義

RESTful Web API は、リソースを中心に編成されています。 リソースに関する API 設計を整理するには、ビジネス エンティティにマップするリソース URI を定義します。 可能であれば、動詞 (リソースに対する操作) ではなく、名詞 (リソース) に基づくリソース URI。

たとえば、eコマース システムでは、主要なビジネス エンティティが顧客と注文である可能性があります。 注文を作成するために、クライアントは HTTP POST 要求の注文情報をリソース URI に送信します。 要求に対する HTTP 応答は、注文の作成が成功したかどうかを示します。

注文リソースを作成するための URI は、次のようになります。

https://api.contoso.com/orders // Good

URI で動詞を使用して操作を表さないようにします。 たとえば、次の URI は推奨されません。

https://api.contoso.com/create-order // Avoid

エンティティは、多くの場合、顧客や注文などのコレクションにグループ化されます。 コレクションはコレクション内の項目とは別のリソースであるため、独自の URI を持つ必要があります。 たとえば、次の URI は注文のコレクションを表します。

https://api.contoso.com/orders

クライアントはコレクションを取得した後、各項目の URI に対して GET 要求を行うことができます。 たとえば、特定の注文に関する情報を受信するために、クライアントは URI https://api.contoso.com/orders/1 に対して HTTP GET 要求を実行し、内部注文データのリソース表現として次の JSON 本文を受信します。

{"orderId":1,"orderValue":99.9,"productId":1,"quantity":1}

リソース URI の名前付け規則

RESTful Web API を設計するときは、リソースの適切な名前付け規則とリレーションシップ規則を使用することが重要です。

  • リソース名には名詞を使用します。 名詞を使用してリソースを表します。 たとえば、/orders の代わりに /create-order を使用します。 HTTP GET、POST、PUT、PATCH、DELETE の各メソッドは、既に言葉によるアクションを意味します。

  • 複数形の名詞を使用してコレクション URI に名前を付けます。 一般に、コレクションを参照する URI には複数形の名詞を使用するのに役立ちます。 コレクションと項目の URI を階層に整理することをお勧めします。 たとえば、 /customers は顧客のコレクションへのパスであり、 /customers/5 は ID が 5 の顧客へのパスです。 このアプローチは、Web API を直感的に保つのに役立ちます。 また、多くの Web API フレームワークでは、パラメーター化された URI パスに基づいて要求をルーティングできるため、パス /customers/{id}のルートを定義できます。

  • さまざまな種類のリソース間の関係と、これらの関連付けを公開する方法を検討してください。 たとえば、 /customers/5/orders は顧客 5 のすべての注文を表します。 注文から顧客への関連付けを表すことで、リレーションシップに逆の方向にアプローチすることもできます。 このシナリオでは、URI が /orders/99/customer可能性があります。 ただし、このモデルを拡張しすぎると、実装が煩雑になる可能性があります。 クライアントが関連リソースに簡単にアクセスできるように、HTTP 応答メッセージの本文にリンクを含める方が良い方法です。 ハイパーテキストをアプリケーション状態のエンジン (HATEOAS) として使用して、関連リソースへのナビゲーションを有効にする 方法については、このメカニズムについて詳しく説明します。

  • リレーションシップをシンプルかつ柔軟に保ちます。 より複雑なシステムでは、クライアントが複数のレベルのリレーションシップ ( /customers/1/orders/99/productsなど) 間を移動できるようにする URI を提供する傾向があります。 ただし、このレベルの複雑さは維持するのが難しい場合があり、将来リソース間の関係が変化する場合は柔軟性に欠けます。 代わりに、URI を比較的単純に保つようにしてください。 アプリケーションがリソースへの参照を持った後、この参照を使用してそのリソースに関連する項目を見つけることができます。 上記のクエリを URI /customers/1/orders に置き換えて顧客 1 のすべての注文を検索し、 /orders/99/products を使用してこの順序で製品を検索できます。

    ヒント

    コレクション/項目/コレクションよりも複雑なリソース URI を必要としないようにします。

  • 大量の小さなリソースを避けてください。 すべての Web 要求は、Web サーバーに負荷を課します。 要求が多いほど、負荷が大きくなります。 多数の小さなリソースを公開する Web API は、 おしゃべりな Web API と呼ばれます。 クライアント アプリケーションが複数の要求を送信して、必要なすべてのデータを検索する必要があるため、これらの API を回避してください。 代わりに、データを非正規化し、関連情報を 1 つの要求で取得できるより大きなリソースに結合することを検討してください。 この方法を実行するには、クライアントが必要としないデータを取得することによるオーバーヘッドとのバランスを取る必要があります。 オブジェクトを大きく取得すると、要求の待機時間が長くなり、帯域幅のコストが増加する可能性があります。 これらのパフォーマンスのアンチパターンの詳細については、「 Chatty I/O余分なフェッチ」を参照してください。

  • データベースの内部構造をミラー化する API は作成しないでください。 REST の目的は、ビジネス エンティティと、アプリケーションがそれらのエンティティに対して実行できる操作をモデル化することです。 クライアントを内部実装に公開しないでください。 たとえば、データがリレーショナル データベースに格納されている場合、Web API は各テーブルをリソースのコレクションとして公開する必要はありません。 このアプローチにより、攻撃対象領域が増加し、データ漏えいが発生する可能性があります。 代わりに、Web API をデータベースの抽象化と考えてください。 必要に応じて、データベースと Web API の間にマッピング レイヤーを導入します。 このレイヤーにより、クライアント アプリケーションが基になるデータベース スキーマへの変更から確実に分離されます。

ヒント

Web API によって実装されたすべての操作を特定のリソースにマップできない場合があります。 関数を呼び出し、結果を HTTP 応答メッセージとして返す HTTP 要求を使用して、これらの リソース以外 のシナリオを処理できます。

たとえば、加算や減算などの単純な電卓操作を実装する Web API では、これらの操作を擬似リソースとして公開し、クエリ文字列を使用して必要なパラメーターを指定する URI を提供できます。 URI /add?operand1=99&operand2=1 に対する GET 要求は、値 100 を含む本文を含む応答メッセージを返します。

ただし、これらの形式の URI は控えめに使用する必要があります。

RESTful Web API メソッドを定義する

RESTful Web API メソッドは、HTTP プロトコルによって定義された要求メソッドとメディアの種類と一致します。 このセクションでは、RESTful Web API で使用される最も一般的な要求メソッドとメディアの種類について説明します。

HTTP 要求メソッド

HTTP プロトコルは、リソースに対して実行するアクションを示す多くの要求メソッドを定義します。 RESTful Web API で使用される最も一般的な方法は、 GETPOSTPUTPATCHDELETE です。 各メソッドは、特定の操作に対応します。 RESTful Web API を設計する場合は、プロトコル定義、アクセスされるリソース、実行されているアクションと一致する方法でこれらのメソッドを使用します。

特定の要求メソッドの効果は、リソースがコレクションか個々の項目かに依存する必要があります。 次の表に、ほとんどの RESTful 実装で使用される規則をいくつか示します。

重要

次の表では、e コマース customer エンティティの例を使用します。 Web API では、すべての要求メソッドを実装する必要はありません。 実装するメソッドは、特定のシナリオによって異なります。

リソース 投稿 取得 置く 削除
/顧客 新しい顧客の作成 すべての顧客を取得する 顧客を一括更新 すべての顧客を削除する
/customers/1 エラー 顧客 1 の詳細を取得する 顧客 1 の詳細 (存在する場合) を更新する 顧客を削除する 1
/顧客/1/注文 顧客 1 の新しい注文を作成する 顧客 1 のすべての注文を取得する 顧客 1 の注文の一括更新 顧客 1 のすべての注文を削除する

GET 要求

GET 要求は、指定された URI でリソースの表現を取得します。 応答メッセージの本文には、要求されたリソースの詳細が含まれています。

GET 要求は、次のいずれかの HTTP 状態コードを返す必要があります。

HTTP 状態コード 理由
200 (OK) メソッドはリソースを正常に返しました。
204 (コンテンツなし) 検索要求が HTTP 応答で一致を返さない場合など、応答本文にはコンテンツが含まれません。
404 (見つかりません) 要求されたリソースが見つかりません。

POST 要求

POST 要求でリソースを作成する必要があります。 サーバーは新しいリソースの URI を割り当て、その URI をクライアントに返します。

重要

POST 要求の場合、クライアントは独自の URI を作成しないでください。 クライアントはコレクションの URI に要求を送信し、サーバーは新しいリソースに URI を割り当てる必要があります。 クライアントが独自の URI の作成を試み、特定の URI に POST 要求を発行すると、サーバーは HTTP 状態コード 400 (BAD REQUEST) を返して、メソッドがサポートされていないことを示します。

RESTful モデルでは、POST 要求を使用して、URI が識別するコレクションに新しいリソースを追加します。 ただし、POST 要求を使用して、新しいリソースを作成せずに、既存のリソースに処理用のデータを送信することもできます。

POST 要求は、次のいずれかの HTTP 状態コードを返す必要があります。

HTTP 状態コード 理由
200 (OK) このメソッドは処理を行いましたが、新しいリソースは作成しません。 操作の結果は、応答本文に含まれる場合があります。
201 (作成済み) リソースが正常に作成されました。 新しいリソースの URI は、応答の Location ヘッダーに含まれます。 応答本文には、リソースの表現が含まれています。
204 (コンテンツなし) 応答本文にコンテンツが含まれていません。
400 (無効な要求) クライアントが要求に無効なデータを配置しました。 応答本文には、エラーに関する詳細情報、または詳細を提供する URI へのリンクを含めることができます。
405 (メソッドは許可されていません) クライアントが POST 要求をサポートしていない URI に対して POST 要求を行おうとしました。

PUT 要求

PUT 要求では、既存のリソースが存在する場合は更新する必要があります。存在しない場合は、場合によっては新しいリソースを作成する必要があります。 PUT 要求を行うには:

  1. クライアントはリソースの URI を指定し、リソースの完全な表現を含む要求本文を含みます。
  2. クライアントが要求を行います。
  3. この URI を持つリソースが既に存在する場合は、置き換えられます。 それ以外の場合、ルートでサポートされている場合は、新しいリソースが作成されます。

PUT メソッドは、コレクションではなく、特定の顧客などの個々の項目であるリソースに適用されます。 サーバーは更新プログラムをサポートしているが、PUT による作成はサポートしない場合があります。 PUT を使用した作成をサポートするかどうかは、クライアントがリソースが存在する前に URI を意味を持って確実に割り当てることができるかどうかによって異なります。 できない場合は、POST を使用してリソースを作成し、サーバーに URI を割り当てます。 次に、PUT または PATCH を使用して URI を更新します。

重要

PUT 要求はべき等である必要があります。つまり、同じリクエストを何度送信しても、常に同じリソースが同じ値に変更されることを意味します。 クライアントが PUT 要求を再送信した場合、結果は変更されません。 これに対し、POST リクエストと PATCH リクエストが必ずしも同じ結果を保証するわけではありません。

PUT 要求は、次のいずれかの HTTP 状態コードを返す必要があります。

HTTP 状態コード 理由
200 (OK) リソースが正常に更新されました。
201 (作成済み) リソースが正常に作成されました。 応答本文には、リソースの表現が含まれている場合があります。
204 (コンテンツなし) リソースは正常に更新されましたが、応答本文にはコンテンツが含まれません。
409 (競合) リソースの現在の状態と競合しているため、要求を完了できませんでした。

ヒント

コレクション内の複数のリソースに対する更新をバッチ処理できる一括 HTTP PUT 操作の実装を検討してください。 PUT 要求では、コレクションの URI を指定する必要があります。 要求本文では、変更するリソースの詳細を指定する必要があります。 このアプローチは、チャット性の低下とパフォーマンスの向上に役立ちます。

PATCH 要求

PATCH 要求は、既存のリソースに対して部分的な更新を実行します。 クライアントは、リソースの URI を指定します。 要求本文では、リソースに適用する一連の変更を指定します。 クライアントは変更のみを送信し、リソースの表現全体を送信しないため、このメソッドは PUT 要求を使用するよりも効率的です。 PATCH では、サーバーがこのアクションをサポートしている場合は、空または null リソースに対する更新のセットを指定することで、新しいリソースを作成することもできます。

PATCH 要求では、クライアントはパッチ ドキュメントの形式で既存のリソースに一連の更新プログラムを送信します。 サーバーはパッチ ドキュメントを処理して更新を実行します。 パッチ ドキュメントでは、リソース全体を記述する代わりに、適用する一連の変更のみを指定します。 PATCH メソッド RFC 5789 の仕様では、パッチ ドキュメントの特定の形式は定義されていません。 形式は、要求のメディアの種類から推論する必要があります。

JSON は、Web API の最も一般的なデータ形式の 1 つです。 JSON ベースの主なパッチ形式は、JSON パッチと JSON マージ パッチの 2 つです。

JSON マージ パッチは、JSON パッチよりも簡単です。 パッチ ドキュメントの構造は元の JSON リソースと同じですが、変更または追加する必要があるフィールドのサブセットのみが含まれます。 さらに、パッチ ドキュメント内のフィールド値に null を指定することで、フィールドを削除できます。 この仕様は、元のリソースが明示的な null 値を持つ可能性がある場合、マージ パッチが適していないことを意味します。

たとえば、元のリソースに次の JSON 表現があるとします。

{
    "name":"gizmo",
    "category":"widgets",
    "color":"blue",
    "price":10
}

このリソースに対して可能な JSON マージ パッチを次に示します。

{
    "price":12,
    "color":null,
    "size":"small"
}

このマージ パッチは、 priceの更新、 colorの削除、 sizeの追加をサーバーに指示します。 namecategoryの値は変更されません。 JSON マージ パッチの詳細については、 RFC 7396 を参照してください。 JSON マージ パッチのメディアの種類が application/merge-patch+json

元のリソースに明示的な null 値を含めることができる場合、パッチドキュメント内の null の特別な意味があるため、マージパッチは適していません。 パッチ ドキュメントでは、サーバーが更新プログラムを適用する順序も指定されていません。 この順序が重要かどうかは、データとドメインによって異なります。 RFC 6902 で定義されている JSON パッチは、変更を適用する一連の操作 (値を検証するための追加、削除、置換、コピー、テストなど) として指定するため、より柔軟です。 JSON パッチのメディアの種類は application/json-patch+json

PATCH 要求は、次のいずれかの HTTP 状態コードを返す必要があります。

HTTP 状態コード 理由
200 (OK) リソースが正常に更新されました。
400 (無効な要求) 形式が誤っているパッチ文書。
409 (競合) パッチ ドキュメントは有効ですが、変更を現在の状態のリソースに適用することはできません。
415 (サポートされていないメディアの種類) パッチ ドキュメント形式はサポートされていません。

DELETE 要求

DELETE 要求は、指定された URI のリソースを削除します。 DELETE 要求は、次のいずれかの HTTP 状態コードを返す必要があります。

HTTP 状態コード 理由
204 (コンテンツなし) リソースが正常に削除されました。 プロセスは正常に処理され、応答本文にはそれ以上の情報が含まれていません。
404 (見つかりません) リソースが存在しません。

リソース MIME の種類

リソース表現は、URI によって識別されるリソースを、XML や JSON などの特定の形式で HTTP プロトコル経由でエンコードおよび転送する方法です。 特定のリソースを取得するクライアントは、API への要求で URI を使用する必要があります。 API は、URI によって示されるデータのリソース表現を返すことによって応答します。

HTTP プロトコルでは、リソース表現形式はメディアの種類 (MIME タイプとも呼ばれます) を使用して指定されます。 非バイナリ データの場合、ほとんどの Web API では JSON (メディアの種類 = application/json) と XML (メディアの種類 = application/xml) がサポートされます。

要求または応答の Content-Type ヘッダーは、リソース表現形式を指定します。 次の例は、JSON データを含む POST 要求を示しています。

POST https://api.contoso.com/orders
Content-Type: application/json; charset=utf-8
Content-Length: 57

{"Id":1,"Name":"Gizmo","Category":"Widgets","Price":1.99}

サーバーがメディアの種類をサポートしていない場合は、HTTP 状態コード 415 (サポートされていないメディアの種類) を返す必要があります。

クライアント要求には、クライアントがサーバーから応答メッセージで受け入れるメディアの種類の一覧を含む Accept ヘッダーを含めることができます。 例えば次が挙げられます。

GET https://api.contoso.com/orders/2
Accept: application/json, application/xml

サーバーが一覧表示されているメディアの種類のいずれにも一致しない場合は、HTTP 状態コード 406 (受け入れられない) を返す必要があります。

非同期メソッドを実装する

POST、PUT、PATCH、または DELETE メソッドでは、完了までに時間がかかる処理が必要な場合があります。 クライアントに応答を送信する前に完了を待つと、許容できない待機時間が発生する可能性があります。 このシナリオでは、メソッドを非同期にすることを検討してください。 非同期メソッドは、HTTP 状態コード 202 (Accepted) を返して、要求が処理のために受け入れられたが不完全であることを示す必要があります。

クライアントが状態エンドポイントをポーリングして状態を監視できるように、非同期要求の状態を返すエンドポイントを公開します。 202 応答の Location ヘッダーに状態エンドポイントの URI を含めます。 例えば次が挙げられます。

HTTP/1.1 202 Accepted
Location: /api/status/12345

クライアントがこのエンドポイントに GET 要求を送信する場合、応答には要求の現在の状態が含まれている必要があります。 必要に応じて、完了までの推定時間または操作を取り消すリンクを含めることができます。

HTTP/1.1 200 OK
Content-Type: application/json

{
    "status":"In progress",
    "link": { "rel":"cancel", "method":"delete", "href":"/api/status/12345" }
}

非同期操作で新しいリソースが作成された場合、状態エンドポイントは、操作の完了後に状態コード 303 (その他を参照) を返す必要があります。 303 応答に、新しいリソースの URI を提供する Location ヘッダーを含めます。

HTTP/1.1 303 See Other
Location: /api/orders/12345

詳細については、「長時間実行される要求に対する非同期サポートを提供する」および「非同期 Request-Reply パターン」を参照してください。

データの改ページとフィルター処理を実装する

データの取得を最適化し、ペイロード サイズを小さくするには、API 設計でデータの改ページとクエリベースのフィルター処理を実装します。 これらの手法を使用すると、クライアントは必要なデータのサブセットのみを要求できるため、パフォーマンスを向上させ、帯域幅の使用量を削減できます。

  • 改ページによって 、大きなデータセットが小さく管理しやすいチャンクに分割されます。 limitなどのクエリ パラメーターを使用して、返す項目の数を指定し、開始点を指定offsetします。 また、limitoffsetなど、limit=25offset=0に意味のある既定値も指定してください。 例えば次が挙げられます。

    GET /orders?limit=25&offset=50
    
    • limit: 返す項目の最大数を指定します。

      ヒント

      サービス拒否攻撃を防ぐには、返される項目の数に上限を課すことをご検討ください。 たとえば、サービスが max-limit=25を設定し、クライアントが limit=1000を要求した場合、API ドキュメントに応じて、サービスは 25 項目または HTTP BAD-REQUEST エラーを返すことができます。

    • offset: データの開始インデックスを指定します。

  • フィルター処理 を使用すると、クライアントは条件を適用してデータセットを絞り込むことができます。 API を使用すると、クライアントは URI のクエリ文字列にフィルターを渡すことができます。

    GET /orders?minCost=100&status=shipped
    
    • minCost: 最小コストが 100 の注文をフィルター処理します。
    • status: 特定の状態の注文をフィルター処理します。

次のベスト プラクティスを検討してください。

  • 並べ替えにより、クライアントはsortなどのsort=priceパラメーターを使用してデータを並べ替えることができます。

    重要

    多くのキャッシュ実装がキャッシュデータのキーとして使用するリソース識別子の一部をクエリ文字列パラメーターが形成するため、並べ替え方法はキャッシュに悪影響を及ぼす可能性があります。

  • クライアント定義プロジェクションのフィールドを選択すると、クライアントは、fieldsなどのfields=id,nameパラメーターを使用して、必要なフィールドのみを指定できます。 たとえば、 /orders?fields=ProductID,Quantity などのフィールドのコンマ区切りのリストを受け入れるクエリ文字列パラメーターを使用できます。

API では、要求されたフィールドを検証して、クライアントがそれらのフィールドへのアクセスを許可され、API を介して通常使用できないフィールドが公開されないようにする必要があります。

部分的な応答をサポートする

一部のリソースには、ファイルやイメージなどの大きなバイナリ フィールドが含まれています。 信頼性の低い断続的な接続によって発生する問題を克服し、応答時間を改善するには、大きなバイナリ リソースの部分的な取得をサポートすることを検討してください。

部分的な応答をサポートするには、Web API で大きなリソースに対する GET 要求の Accept-Ranges ヘッダーをサポートする必要があります。 このヘッダーは、GET 操作が部分的な要求をサポートしていることを示します。 クライアント アプリケーションは、バイト範囲として指定されたリソースのサブセットを返す GET 要求を送信できます。

また、これらのリソースに対する HTTP HEAD 要求の実装も検討してください。 HEAD 要求は GET 要求に似ていますが、リソースを記述する HTTP ヘッダーのみを空のメッセージ本文で返す点が異なります。 クライアント アプリケーションは HEAD 要求を発行して、部分的な GET 要求を使用してリソースをフェッチするかどうかを決定できます。 例えば次が挙げられます。

HEAD https://api.contoso.com/products/10?fields=productImage

応答メッセージの例を次に示します。

HTTP/1.1 200 OK

Accept-Ranges: bytes
Content-Type: image/jpeg
Content-Length: 4580

Content-Length ヘッダーはリソースの合計サイズを示し、Accept-Ranges ヘッダーは、対応する GET 操作で部分的な結果がサポートされていることを示します。 クライアント アプリケーションでは、この情報を使用して、より小さなチャンクでイメージを取得できます。 最初の要求では、Range ヘッダーを使用して最初の 2,500 バイトを取得します。

GET https://api.contoso.com/products/10?fields=productImage
Range: bytes=0-2499

応答メッセージは、HTTP 状態コード 206 を返すことによって、この応答が部分的であることを示します。 Content-Length ヘッダーは、リソースのサイズではなく、メッセージ本文で返される実際のバイト数を指定します。 Content-Range ヘッダーは、リソースのどの部分が返されているかを示します (4580 のうちバイト 0 から 2499):

HTTP/1.1 206 Partial Content

Accept-Ranges: bytes
Content-Type: image/jpeg
Content-Length: 2500
Content-Range: bytes 0-2499/4580

[...]

クライアント アプリケーションからの後続の要求は、リソースの残りの部分を取得できます。

HATEOAS を実装する

REST を使用する主な理由の 1 つは、URI スキーマに関する事前の知識がなくても、リソースのセット全体を移動できることです。 各 HTTP GET 要求は、応答に含まれるハイパーリンクを使用して、要求されたオブジェクトに直接関連するリソースを検索するために必要な情報を返す必要があります。 要求には、これらの各リソースで使用可能な操作を説明する情報も指定する必要があります。 この原則は HATEOAS と呼ばれ、アプリケーション状態のエンジンとしてハイパーテキストと呼ばれます。 システムは実質的に有限のステート マシンであり、各要求への応答には、ある状態から別の状態に移動するために必要な情報が含まれています。 その他の情報は必要ありません。

HATEOAS 原則をモデル化する方法を定義する汎用標準はありません。 このセクションの例では、1 つの可能な独自のソリューションを示します。

たとえば、注文と顧客の関係を処理するために、注文の表現には、注文の顧客に対して使用可能な操作を識別するリンクが含まれる場合があります。 次のコード ブロックは、考えられる表現です。

{
  "orderID":3,
  "productID":2,
  "quantity":4,
  "orderValue":16.60,
  "links":[
    {
      "rel":"customer",
      "href":"https://api.contoso.com/customers/3",
      "action":"GET",
      "types":["text/xml","application/json"]
    },
    {
      "rel":"customer",
      "href":"https://api.contoso.com/customers/3",
      "action":"PUT",
      "types":["application/x-www-form-urlencoded"]
    },
    {
      "rel":"customer",
      "href":"https://api.contoso.com/customers/3",
      "action":"DELETE",
      "types":[]
    },
    {
      "rel":"self",
      "href":"https://api.contoso.com/orders/3",
      "action":"GET",
      "types":["text/xml","application/json"]
    },
    {
      "rel":"self",
      "href":"https://api.contoso.com/orders/3",
      "action":"PUT",
      "types":["application/x-www-form-urlencoded"]
    },
    {
      "rel":"self",
      "href":"https://api.contoso.com/orders/3",
      "action":"DELETE",
      "types":[]
    }]
}

この例では、 links 配列には一連のリンクがあります。 各リンクは、関連エンティティに対する操作を表します。 各リンクのデータには、リレーションシップ ("customer")、URI (https://api.contoso.com/customers/3)、HTTP メソッド、サポートされている MIME の種類が含まれます。 クライアント アプリケーションは、操作を呼び出すためにこの情報を必要とします。

links配列には、取得したリソースに関する自己参照情報も含まれます。 これらのリンクには、 自己関係があります。

返されるリンクのセットは、リソースの状態に応じて変更される可能性があります。 ハイパーテキストが アプリケーションの状態のエンジン であるという考え方は、このシナリオを説明します。

バージョン管理を実装する

Web API は静的なままではありません。 ビジネス要件が変更されると、リソースの新しいコレクションが追加されます。 新しいリソースが追加されると、リソース間の関係が変わる可能性があり、リソース内のデータの構造が修正される可能性があります。 新しい要件や異なる要件を処理するために Web API を更新するのは簡単なプロセスですが、Web API を使用するクライアント アプリケーションに対してこのような変更が及ぼす影響を考慮する必要があります。 Web API を設計して実装する開発者は、その API を完全に制御できますが、パートナー組織によって構築されたクライアント アプリケーションを同じレベルで制御することはできません。 新しいクライアント アプリケーションで新しい機能とリソースを使用できるようにしながら、既存のクライアント アプリケーションを引き続きサポートすることが重要です。

バージョン管理を実装する Web API は、それが公開する機能とリソースを示すことができます。また、クライアント アプリケーションは、特定のバージョンの機能またはリソースに送信される要求を送信できます。 次のセクションでは、いくつかの異なるアプローチについて説明します。それぞれに独自の利点とトレードオフがあります。

バージョン管理なし

この方法は最も簡単で、一部の内部 API で使用できます。 重要な変更は、新しいリソースまたは新しいリンクとして表すことができます。 既存のリソースにコンテンツを追加しても、このコンテンツが表示されないクライアント アプリケーションでは無視されるため、重大な変更が発生しない可能性があります。

たとえば、URI https://api.contoso.com/customers/3 に対する要求は、クライアント アプリケーションが期待する idname、および address フィールドを含む 1 人の顧客の詳細を返す必要があります。

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{"id":3,"name":"Fabrikam, Inc.","address":"1 Microsoft Way Redmond WA 98053"}

わかりやすくするために、このセクションで示す応答例には HATEOAS リンクは含まれていません。

DateCreated フィールドが顧客リソースのスキーマに追加された場合、応答は次のようになります。

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{"id":3,"name":"Fabrikam, Inc.","dateCreated":"2025-03-22T12:11:38.0376089Z","address":"1 Microsoft Way Redmond WA 98053"}

認識できないフィールドを無視できる場合、既存のクライアント アプリケーションは引き続き正常に機能する可能性があります。 一方、新しいクライアント アプリケーションは、この新しいフィールドを処理するように設計できます。 ただし、フィールドの削除や名前変更など、リソースのスキーマの大幅な変更が発生する可能性があります。 または、リソース間の関係が変わる可能性があります。 これらの更新プログラムは、既存のクライアント アプリケーションが正しく機能することを妨げる破壊的変更を構成できます。 これらのシナリオでは、次のいずれかの方法を検討してください。

URI のバージョン管理

Web API を変更したり、リソースのスキーマを変更したりするたびに、各リソースの URI にバージョン番号を追加します。 以前の既存の URI は、元のスキーマに準拠するリソースを返すことで、引き続き正常に動作する必要があります。

たとえば、前の例の address フィールドは、アドレスの各構成要素 ( streetAddresscitystatezipCodeなど) を含むサブフィールドに再構築されます。 このバージョンのリソースは、 https://api.contoso.com/v2/customers/3などのバージョン番号を含む URI を介して公開できます。

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{"id":3,"name":"Fabrikam, Inc.","dateCreated":"2025-03-22T12:11:38.0376089Z","address":{"streetAddress":"1 Microsoft Way","city":"Redmond","state":"WA","zipCode":98053}}

このバージョン管理メカニズムは単純ですが、要求を適切なエンドポイントにルーティングするサーバーによって異なります。 ただし、Web API が何度か繰り返し成熟し、サーバーがさまざまなバージョンをサポートする必要があるため、扱いにくい場合があります。 純粋主義者の観点からすれば、すべての場合において、クライアントアプリケーションは同じデータ(顧客3)を取得するので、URIはバージョンによって変わるべきではありません。 このスキーマでは、すべてのリンクが URI にバージョン番号を含める必要があるため、HATEOAS の実装も複雑になります。

クエリ文字列のバージョン管理

複数の URI を指定する代わりに、HTTP 要求に追加されたクエリ文字列内のパラメーター ( https://api.contoso.com/customers/3?version=2 など) を使用して、リソースのバージョンを指定できます。 バージョン パラメーターは、古いクライアント アプリケーションで省略されている場合は、既定で意味のある値 (1 など) にする必要があります。

この方法には、同じリソースが常に同じ URI から取得されるというセマンティック上の利点があります。 ただし、このメソッドは、クエリ文字列を解析して適切な HTTP 応答を返す要求を処理するコードに依存します。 この方法では、URI のバージョン管理メカニズムと同じ方法で HATEOAS の実装も複雑になります。

一部の古い Web ブラウザーと Web プロキシでは、URI にクエリ文字列を含む要求の応答がキャッシュされません。 キャッシュされていない応答により、Web API を使用し、古い Web ブラウザー内から実行される Web アプリケーションのパフォーマンスが低下する可能性があります。

ヘッダーのバージョン管理

バージョン番号をクエリ文字列パラメーターとして追加する代わりに、リソースのバージョンを示すカスタム ヘッダーを実装できます。 この方法では、クライアント アプリケーションが要求に適切なヘッダーを追加する必要があります。 ただし、バージョン ヘッダーを省略すると、クライアント要求を処理するコードでは、バージョン 1 などの既定値を使用できます。

次の例では、 Custom-Header という名前のカスタム ヘッダーを使用します。 このヘッダーの値は、Web API のバージョンを示します。

バージョン 1:

GET https://api.contoso.com/customers/3
Custom-Header: api-version=1
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{"id":3,"name":"Fabrikam, Inc.","address":"1 Microsoft Way Redmond WA 98053"}

バージョン 2:

GET https://api.contoso.com/customers/3
Custom-Header: api-version=2
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{"id":3,"name":"Fabrikam, Inc.","dateCreated":"2025-03-22T12:11:38.0376089Z","address":{"streetAddress":"1 Microsoft Way","city":"Redmond","state":"WA","zipCode":98053}}

URI のバージョン管理クエリ文字列のバージョン管理と同様に、HATEOAS を実装するには、リンクに適切なカスタム ヘッダーを含める必要があります。

メディアの種類のバージョン管理

クライアント アプリケーションは、HTTP GET 要求を Web サーバーに送信するときに、Accept ヘッダーを使用して、処理できるコンテンツの形式を指定する必要があります。 通常、Accept ヘッダーの目的は、クライアント アプリケーションが応答の本文を XML、JSON、またはクライアントが解析できるその他の一般的な形式にするかどうかを指定できるようにすることです。 ただし、クライアント アプリケーションが必要とするリソースのバージョンを示す情報を含むカスタム メディアの種類を定義できます。

次の例は、値が application/vnd.contoso.v1+json の Accept ヘッダーを指定する要求を示しています。 vnd.contoso.v1要素は、リソースのバージョン 1 を返す必要があることを Web サーバーに示します。 json要素は、応答本文の形式を JSON にすることを指定します。

GET https://api.contoso.com/customers/3
Accept: application/vnd.contoso.v1+json

要求を処理するコードは、Accept ヘッダーを処理し、可能な限りそれを受け入れる役割を担います。 クライアント アプリケーションでは、Accept ヘッダーで複数の形式を指定できます。これにより、Web サーバーは応答本文に最適な形式を選択できます。 Web サーバーは、Content-Type ヘッダーを使用して、応答本文のデータの形式を確認します。

HTTP/1.1 200 OK
Content-Type: application/vnd.contoso.v1+json; charset=utf-8

{"id":3,"name":"Fabrikam, Inc.","address":"1 Microsoft Way Redmond WA 98053"}

Accept ヘッダーで既知のメディアの種類が指定されていない場合、Web サーバーは HTTP 406 (受け入れられない) 応答メッセージを生成するか、既定のメディアの種類のメッセージを返すことができます。

このバージョン管理メカニズムは単純で、HATEOAS に適しており、リソース リンクに関連データの MIME の種類を含めることができます。

バージョン管理戦略を選択すると、特に Web サーバーのキャッシュに関連する影響が発生します。 URI のバージョン管理とクエリ文字列のバージョン管理スキーマは、毎回同じ URI またはクエリ文字列の組み合わせが同じデータを参照するため、キャッシュフレンドリです。

ヘッダーのバージョン管理とメディアの種類のバージョン管理メカニズムでは、通常、カスタム ヘッダーまたは Accept ヘッダーの値を調べるには、より多くのロジックが必要です。 大規模な環境では、異なるバージョンの Web API を使用する多くのクライアントによって、サーバー側キャッシュに大量のデータが重複する可能性があります。 この問題は、クライアント アプリケーションがキャッシュを実装するプロキシを介して Web サーバーと通信し、要求されたデータのコピーがキャッシュに現在含まれていない場合にのみ Web サーバーに転送する場合に深刻になる可能性があります。

マルチテナント Web API

マルチテナント Web API ソリューションは、独自のユーザー グループを持つ個別の組織など、複数のテナントによって共有されます。

マルチテナントは、1 つの Web API 内の複数のテナント間でリソースにアクセスして検出する方法を決定するため、Web API の設計に大きく影響します。 分離、スケーラビリティ、またはテナント固有のカスタマイズを実装するための将来のリファクタリングの必要性を回避するために、マルチテナントを念頭に置いて API を設計します。

適切に設計された API では、サブドメイン、パス、ヘッダー、トークンを使用して、要求でテナントを識別する方法を明確に定義する必要があります。 この構造により、システム内のすべてのユーザーに一貫した柔軟なエクスペリエンスが保証されます。 詳細については、マルチテナント ソリューションにおけるテナントへの要求のマッピングに関するページを参照してください。

マルチテナントは、エンドポイントの構造、要求の処理、認証、および承認に影響します。 この方法は、API ゲートウェイ、ロード バランサー、バックエンド サービスによる要求のルーティングと処理方法にも影響します。 次の戦略は、Web API でマルチテナントを実現するための一般的な方法です。

サブドメインまたはドメインベースの分離を使用する (DNS レベルのテナント)

この方法では、 テナント固有のドメインを使用して要求をルーティングします。 ワイルドカード ドメインは、柔軟性と簡潔さを実現するためにサブドメインを使用します。 テナントが独自のドメインを使用できるようにするカスタム ドメインは、より詳細な制御を提供し、特定のニーズに合わせて調整できます。 どちらの方法も、適切なインフラストラクチャにトラフィックを転送するために、 A レコードや CNAME レコードを含む適切な DNS 構成に依存します。 ワイルドカード ドメインは構成を簡略化しますが、カスタム ドメインはよりブランド化されたエクスペリエンスを提供します。

リバース プロキシとバックエンド サービスの間でホスト名を保持して、URL リダイレクトなどの問題を回避し、内部 URL を公開しないようにします。 この方法により、テナント固有のトラフィックの正しいルーティングが保証され、内部インフラストラクチャの保護に役立ちます。 DNS 解決は、データ所在地を達成し、規制コンプライアンスを確保するために不可欠です。

GET https://adventureworks.api.contoso.com/orders/3

テナント固有の HTTP ヘッダーを渡す

テナント情報は、 X-Tenant-IDX-Organization-ID などのカスタム HTTP ヘッダーを介して、または HostX-Forwarded-Hostなどのホスト ベースのヘッダーを介して渡すことも、JSON Web トークン (JWT) 要求から抽出することもできます。 選択は API ゲートウェイまたはリバース プロキシのルーティング機能によって異なります。ヘッダーベースのソリューションでは、各要求を検査するためにレイヤー 7 (L7) ゲートウェイが必要です。 この要件により、処理オーバーヘッドが増加し、トラフィックのスケーリング時にコンピューティング コストが増加します。 ただし、ヘッダーベースの分離には主な利点があります。 これにより、一元化された認証が可能になり、マルチテナント API 全体のセキュリティ管理が簡略化されます。 SDK または API クライアントを使用すると、テナント コンテキストが実行時に動的に管理されるため、クライアント側の構成の複雑さが軽減されます。 また、ヘッダーにテナント コンテキストを保持すると、URI 内のテナント固有のデータを回避することで、よりクリーンで RESTful な API 設計が実現します。

ヘッダーベースのルーティングに関する重要な考慮事項は、キャッシュ レイヤーが URI ベースのキーのみに依存し、ヘッダーを考慮しない場合に、キャッシュが複雑になる点です。 ほとんどのキャッシュ メカニズムでは URI 参照が最適化されるため、ヘッダーに依存すると、キャッシュ エントリが断片化する可能性があります。 フラグメント化されたエントリにより、キャッシュ ヒットが減少し、バックエンドの負荷が増加します。 さらに重要な点として、キャッシュ レイヤーがヘッダーによって応答を区別しない場合、あるテナントを対象とするキャッシュされたデータを別のテナントに提供し、データ漏えいのリスクを生み出すことができます。

GET https://api.contoso.com/orders/3
X-Tenant-ID: adventureworks

または

GET https://api.contoso.com/orders/3
Host: adventureworks

または

GET https://api.contoso.com/orders/3
Authorization: Bearer <JWT-token including a tenant-id: adventureworks claim>

URI パスを介してテナント固有の情報を渡す

この方法では、リソース階層内のテナント識別子が追加され、API ゲートウェイまたはリバース プロキシに依存して、パス セグメントに基づいて適切なテナントが決定されます。 パスベースの分離は効果的ですが、Web API の RESTful 設計が損なわれ、より複雑なルーティング ロジックが導入されます。 多くの場合、URI パスを解析して正規化するには、パターン マッチングまたは正規表現が必要です。

これに対し、ヘッダーベースの分離は、キーと値のペアとして HTTP ヘッダーを介してテナント情報を伝達します。 どちらの方法でも、効率的なインフラストラクチャ共有を実現して運用コストを削減し、大規模なマルチテナント Web API のパフォーマンスを向上させることができます。

GET https://api.contoso.com/tenants/adventureworks/orders/3

API で分散トレースとトレース コンテキストを有効にする

分散システムとマイクロサービス アーキテクチャが標準になると、 最新のアーキテクチャの複雑さが増しますCorrelation-IDX-Request-IDX-Trace-IDなどのヘッダーを使用して API 要求にトレース コンテキストを伝達することは、エンドツーエンドの可視性を実現するためのベスト プラクティスです。 この方法により、クライアントからバックエンド サービスに送信される要求をシームレスに追跡できます。 エラーの迅速な識別を容易にし、待機時間を監視し、サービス間で API の依存関係をマップします。

トレースとコンテキスト情報を含めることをサポートする API は、監視レベルとデバッグ機能を強化します。 分散トレースを有効にすることで、これらの API を使用すると、システムの動作をより詳細に理解し、複雑な複数のサービス環境で問題を追跡、診断、解決しやすくなります。

GET https://api.contoso.com/orders/3
Correlation-ID: 0f8fad5b-d9cb-469f-a165-70867728950e
HTTP/1.1 200 OK
...
Correlation-ID: 0f8fad5b-d9cb-469f-a165-70867728950e

{...}

Web API 成熟度モデル

2008 年、レオナルド・リチャードソンは、Web API のリチャードソン成熟度モデル (RMM) と呼ばれるものを提案しました。 RMM は、Web API の 4 つの成熟度レベルを定義し、Web サービスを設計するためのアーキテクチャ アプローチとしての REST の原則に基づいています。 RMM では、成熟度のレベルが上がるにつれて、API はよりRESTfulになり、RESTの原則により厳密に準拠します。

レベルは次のとおりです。

  • レベル 0: 1 つの URI を定義します。すべての操作は、この URI に対する POST 要求です。 通常、単純オブジェクト アクセス プロトコル Web サービスはこのレベルです。
  • レベル 1: 個々のリソースに対して個別の URI を作成します。 このレベルはまだ RESTful ではありませんが、RESTful 設計に合わせ始めます。
  • レベル 2: HTTP メソッドを使用して、リソースに対する操作を定義します。 実際には、発行された Web API の多くは、ほぼこのレベルに合わせて調整されます。
  • レベル 3: ハイパーメディア (HATEOAS) を使用します。 このレベルは、Fielding の定義に従って、本当に RESTful API です。

OpenAPI イニシアティブ

OpenAPI イニシアチブは、ベンダー間で REST API の説明を標準化するために、業界コンソーシアムによって作成されました。 標準化仕様は、OpenAPI イニシアティブの下に持ち込まれ、OpenAPI 仕様 (OAS) に名前が変更される前に Swagger と呼ばれていました。

RESTful Web API に OpenAPI を採用することもできます。 次の点を考慮してください。

  • OAS には、REST API 設計に関する一連のガイドラインが付属しています。 ガイドラインは相互運用性に有利ですが、設計が仕様に準拠していることを確認する必要があります。

  • OpenAPI は、実装優先のアプローチではなく、コントラクト優先のアプローチを促進します。 コントラクト優先とは、最初に API コントラクト (インターフェイス) を設計してから、コントラクトを実装するコードを記述することを意味します。

  • Swagger (OpenAPI) などのツールは、API コントラクトからクライアント ライブラリまたはドキュメントを生成できます。 例については、 Swagger/OpenAPI ASP.NET Core Web API のドキュメントを参照してください。

次のステップ