web-dev-qa-db-ja.com

継承を使用してRESTful APIをモデル化する方法は?

RESTful APIを介して公開する必要があるオブジェクト階層があり、URLがどのように構成され、何が返されるかがわかりません。ベストプラクティスが見つかりませんでした。

私は動物から継承する犬と猫がいるとしましょう。犬と猫のCRUD操作が必要です。また、一般的な動物の手術ができるようになりたいです。

私の最初のアイデアは、このようなことをすることでした:

GET /animals        # get all animals
POST /animals       # create a dog or cat
GET /animals/123    # get animal 123

問題は、/ animalsコレクションが「矛盾」していることです。これは、まったく同じ構造を持たないオブジェクト(犬と猫)を返すことができるためです。異なる属性を持つオブジェクトを返すコレクションを持つことは「RESTful」と見なされますか?

別の解決策は、次のように、具体的なタイプごとにURLを作成することです。

GET /dogs       # get all dogs
POST /dogs      # create a dog
GET /dogs/123   # get dog 123

GET /cats       # get all cats
POST /cats      # create a cat
GET /cats/123   # get cat 123

しかし、今では犬と猫の関係は失われています。すべての動物を取得したい場合は、犬と猫の両方のリソースを照会する必要があります。 URLの数も、新しい動物のサブタイプごとに増加します。

別の提案は、これを追加することで2番目のソリューションを強化することでした。

GET /animals    # get common attributes of all animals

この場合、返される動物には、すべての動物に共通の属性のみが含まれ、犬固有の属性と猫固有の属性が削除されます。これにより、詳細は少なくなりますが、すべての動物を取得できます。返される各オブジェクトには、詳細で具体的なバージョンへのリンクを含めることができます。

コメントや提案はありますか?

77
Alpha Hydrae

私はお勧めします:

  • リソースごとに1つのURIのみを使用する
  • 属性レベルのみで動物を区別する

同じリソースに複数のURIを設定することは、混乱や予期しない副作用を引き起こす可能性があるため、決して良いアイデアではありません。そのため、単一のURIは/animalsなどの一般的なスキームに基づいている必要があります。

「ベース」レベルで犬と猫のコレクション全体を処理するという次の課題は、/animals URIアプローチによってすでに解決されています。

犬や猫などの特殊なタイプに対処する最後の課題は、メディアタイプ内のクエリパラメーターと識別属性の組み合わせを使用して簡単に解決できます。例えば:

GET /animalsAccept : application/vnd.vet-services.animals+json

{
   "animals":[
      {
         "link":"/animals/3424",
         "type":"dog",
         "name":"Rex"
      },
      {
         "link":"/animals/7829",
         "type":"cat",
         "name":"Mittens"
      }
   ]
}
  • GET /animals-すべての犬と猫を取得し、レックスとミトンの両方を返します
  • GET /animals?type=dog-すべての犬を取得し、レックスのみを返します
  • GET /animals?type=cat-すべての猫を取得、ミトンのみ

次に、動物を作成または変更するときは、呼び出し元で関連する動物のタイプを指定する必要があります。

メディアタイプ:application/vnd.vet-services.animal+json

{
   "type":"dog",
   "name":"Fido"
}

上記のペイロードは、POSTまたはPUTリクエストで送信できます。

上記のスキームは、OO RESTによる継承と基本的な同様の特性を取得し、大きな手術やURIスキームへの変更なしに、さらに専門化(つまり、より多くの動物の種類)を追加する機能を備えています。

37
Brian Kelly

私は/ animalsに行き、犬と魚の両方のリストを返します:

<animals>
  <animal type="dog">
    <name>Fido</name>
    <fur-color>White</fur-color>
  </animal>
  <animal type="fish">
    <name>Wanda</name>
    <water-type>Salt</water-type>
  </animal>
</animals>

同様のJSONの例を簡単に実装できるはずです。

クライアントは、常に「name」要素が存在することに依存できます(共通の属性)。しかし、「type」属性に応じて、動物表現の一部として他の要素があります。

そのようなリストを返す際に本質的にRESTfulまたはunRESTfulはありません-RESTはデータを表すための特定の形式を規定していません。データがsome表現を持たなければならず、その表現の形式はメディアタイプ(HTTPではContent-Typeヘッダー)によって識別されるということだけです。

ユースケースについて考えてください-混合動物のリストを表示する必要がありますか?それでは、混合動物データのリストを返します。犬のリストだけが必要ですか?まあ、そのようなリストを作成します。

/ animals?type = dogまたは/ dogsを行うかどうかは、URL形式を規定しないRESTには関係ありません。これは、RESTの範囲外の実装の詳細として残されています。 RESTは、リソースに識別子が必要であることのみを示しています。どの形式でもかまいません。

RESTful APIに近づけるには、ハイパーメディアリンクを追加する必要があります。例えば、動物の詳細への参照を追加することにより:

<animals>
  <animal type="dog" href="/animals/123">
    <name>Fido</name>
    <fur-color>White</fur-color>
  </animal>
  <animal type="fish" href="/animals/321">
    <name>Wanda</name>
    <water-type>Salt</water-type>
  </animal>
</animals>

ハイパーメディアリンクを追加することにより、クライアント/サーバーカップリングを削減します。上記の場合、URL構築の負担をクライアントから取り除き、サーバーがURLの構築方法を決定できるようにします(定義上、それが唯一の権限です)。

4
Jørn Wildt

この質問は、OpenAPIの最新バージョンで導入された最近の拡張機能のサポートにより、より適切に回答できます。

JSONスキーマv1.0以降、oneOf、allOf、anyOfなどのキーワードを使用してスキーマを結合し、検証されたメッセージペイロードを取得することが可能になりました。

https://spacetelescope.github.io/understanding-json-schema/reference/combining.html

ただし、OpenAPI(以前のSwagger)では、スキーマの構成がキーワードdiscriminator(v2.0 +)およびoneOf(v3.0 +)によって強化され、真にサポートされています。多型。

https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#schemaComposition

継承は、oneOf(サブタイプの1つを選択するため)とallOf(タイプとそのサブタイプの1つを組み合わせるため)の組み合わせを使用してモデル化できます。以下はPOSTメソッドの定義のサンプルです。

paths:
  /animals:
    post:
      requestBody:
      content:
        application/json:
          schema:
            oneOf:
              - $ref: '#/components/schemas/Dog'
              - $ref: '#/components/schemas/Cat'
              - $ref: '#/components/schemas/Fish'
            discriminator:
              propertyName: animal_type
     responses:
       '201':
         description: Created

components:
  schemas:
    Animal:
      type: object
      required:
        - animal_type
        - name
      properties:
        animal_type:
          type: string
        name:
          type: string
      discriminator:
        property_name: animal_type
    Dog:
      allOf:
        - $ref: "#/components/schemas/Animal"
        - type: object
          properties:
            playsFetch:
              type: string
    Cat:
      allOf:
        - $ref: "#/components/schemas/Animal"
        - type: object
          properties:
            likesToPurr:
              type: string
    Fish:
      allOf:
        - $ref: "#/components/schemas/Animal"
        - type: object
          properties:
            water-type:
              type: string
2
dbaltor

これは古い質問ですが、RESTful継承モデリングのさらなる問題を調査することに興味があります。

私はいつも犬は動物であり、雌鶏でもあると言うことができますが、雌鶏は哺乳類ですが、雌鶏は卵を作るので、できません。のようなAPI

GET animals /:animalID/eggs

(リスコフ置換の結果として)動物のすべてのサブタイプが卵を持つことができることを示すため、一貫性がありません。すべての哺乳類がこのリクエストに「0」で応答するとフォールバックが発生しますが、POSTメソッドも有効にしたらどうなりますか?明日は私のクレープに犬の卵があるのではないかと心配するべきです?

これらのシナリオを処理する唯一の方法は、オブジェクトをダウンキャストするときのように、すべての可能な「派生リソース」間で共有されるすべてのサブリソースを集約する「スーパーリソース」と、それを必要とする各派生リソースの専門化を提供することですおっと

GET/animals /:animalID/sons GET/hens /:animalID/eggs POST/hens /:animalID/eggs

ここでの欠点は、誰かが鶏のコレクションのインスタンスを参照するために犬のIDを渡すことができるが、その犬は鶏ではないため、応答が404または400で理由メッセージが含まれている場合は正しくないことです

私が間違っている?

1
Carmine Ingaldi

しかし、今では犬と猫の関係は失われています。

確かに、URIはオブジェクト間の関係を決して反映しないことに注意してください。

1
G. Demecki