web-dev-qa-db-ja.com

REST複合/複合/ネストされたリソース

RESTベースのAPIで概念に対処するための最良の方法に頭を包もうとしています。他のリソースを含まないフラットリソースは問題ありません。私が問題に直面しているのは、複雑なリソースです。

たとえば、漫画本のリソースがあります。 ComicBookには、authorissue numberdateなどのあらゆる種類のプロパティがあります。

漫画本には、1..nカバーのリストもあります。これらのカバーは複雑なオブジェクトです。表紙に関する多くの情報が含まれています。アーティスト、日付、表紙のベース64エンコードされた画像です。

GET上のComicBookの場合、コミック、およびbase64'edイメージを含むすべてのカバーを返すことができます。たぶん、1冊のコミックを手に入れるのに大したことではないでしょう。しかし、システム内のすべての漫画をテーブルに一覧表示するクライアントアプリを作成しているとします。
テーブルにはComicBookリソースからのいくつかのプロパティが含まれますが、テーブル内のすべてのカバーを表示する必要はありません。それぞれが複数の表紙を備えた1000冊の漫画本を返すと、途方もなく大量のデータが回線を介して送られることになります。その場合、エンドユーザーには不要なデータです。

私の本能は、Coverをリソースにし、ComicBookにカバーを含めることです。したがって、CoverはURIです。コミックブックのGETが機能するようになりました。巨大なCoverリソースの代わりに各表紙のURIを送り返し、クライアントは必要に応じて表紙リソースを取得できます。

今、私は新しいコミックの作成に問題があります。 Comicを作成するときに、少なくとも1つのカバーを作成することは確かです。実際、これはおそらくビジネスルールです。
そのため、今は行き詰まっています。まず、Coverを送信し、その表紙のURIを取得してから、POST_ComicBookを送信することで、クライアントにビジネスルールを強制しますリストにそのURIがある場合、またはPOST上のComicBookが、吐き出すものとは異なる外観のリソースを取り込みます。 POSTおよびGETの着信リソースはディープコピーであり、発信GETには依存リソースへの参照が含まれます。

Coverリソースは、おそらくどのような場合でも必要になるでしょう。なぜなら、クライアントとして対処したいのは、場合によっては方向をカバーしていると確信しているからです。したがって、依存リソースのサイズに関係なく、問題は一般的な形で存在します。一般に、クライアントにそれらのリソースがどのように構成されているかを「認識」させることなく、複雑なリソースをどのように処理しますか?

168
jgerman

@ray、優れた議論

@jgerman、それはRESTであるからといって、POSTからリソースを設定する必要があることを忘れないでください。

リソースの任意の表現に含めることを選択するのはあなた次第です。

個別に参照される表紙のケースは、子リソース(表紙)を相互参照できる親リソース(漫画本)の作成にすぎません。たとえば、著者、出版社、キャラクター、またはカテゴリへの参照を個別に提供することもできます。これらのリソースを個別に作成するか、子リソースとして参照するコミックブックの前に作成することをお勧めします。または、親リソースの作成時に新しい子リソースを作成することもできます。

カバーの特定のケースは、カバーが実際に漫画本を必要とするという点でやや複雑です。

ただし、電子メールメッセージをリソースとして、差出人アドレスを子リソースと見なす場合、明らかに差出人アドレスを個別に参照できます。たとえば、アドレスからすべてを取得します。または、以前の差出人アドレスで新しいメッセージを作成します。電子メールがRESTである場合、多くの相互参照されたリソースが利用できることが簡単にわかります:/ received-messages、/ draft-messages、/ from-addresses、/ to-addresses、/ addresses、/ subjects、/ attachments、/ folders 、/ tags、/ categories、/ labelsなど.

このチュートリアルは、相互参照されたリソースの素晴らしい例を提供します。 http://www.peej.co.uk/articles/restfully-delicious.html

これは、自動生成されたデータの最も一般的なパターンです。たとえば、サーバーによって生成されるため、新しいリソースのURI、ID、または作成日を投稿しないでください。さらに、新しいリソースを取得したときに、URI、ID、または作成日を取得できます。

バイナリデータの場合の例。たとえば、バイナリデータを子リソースとして投稿します。親リソースを取得すると、それらの子リソースを同じバイナリデータとして、またはバイナリデータを表すURIとして表すことができます。

フォームとパラメーターは、リソースのHTML表現とはすでに異なっています。 URLを生成するバイナリ/ファイルパラメーターをポストすることは、ストレッチではありません。

新しいリソース(/ comic-books/new)のフォームを取得するか、リソースを編集するフォーム(/ comic-books/0/edit)を取得すると、リソースのフォーム固有の表現を要求しています。 content-type "application/x-www-form-urlencoded"または "multipart/form-data"でリソースコレクションに投稿する場合、そのタイプ表現を保存するようサーバーに求めています。サーバーは、保存されたHTML表現などで応答できます。

APIまたは同様の目的のために、HTML、XML、またはJSON表現をリソースコレクションにポストすることもできます。

また、コミックブックの後に投稿されたカバーを考慮に入れながら、説明するようにリソースとワークフローを表現することもできますが、カバーにはコミックブックが必要です。次の例。

  • カバーの作成を遅らせることができます
  • 必要な表紙で漫画本を作成できます
  • カバーを相互参照できます
  • 複数のカバーが可能
  • ドラフト漫画本を作成する
  • ドラフト漫画本の表紙を作成する
  • ドラフト漫画本を発行

GET/comic-books
=> 200 OK、すべてのコミックを入手してください。

GET/comic-books/0
=> 200 OK、カバー(/ covers/1、/ covers/2)でコミックブック(id:0)を取得します。

GET/comic-books/0/covers
=> 200 OK、コミックブックの表紙を入手してください(id:0)。

GET/covers
=> 200 OK、すべてのカバーを入手してください。

GET/covers/1
=> 200 OK、コミックブック(/ comic-books/0)で表紙(id:1)を取得します。

GET/comic-books/new
=> 200 OK、コミックブックを作成するフォームを取得します(フォーム:POST/draft-comic-books)。

POST/draft-comic-books
title = foo
author = boo
publisher = goo
published = 2011-01-01
=> 302検出、場所:/ draft-comic-books/3、カバー付きのドラフト漫画本(id:3)にリダイレクト(バイナリ)。

GET/draft-comic-books/3
=> 200 OK、カバー付きのドラフト漫画本(id:3)を取得します。

GET/draft-comic-books/3/covers
=> 200 OK、ドラフト漫画本のカバーを入手(/ draft-comic-book/3)。

GET/draft-comic-books/3/covers/new
=> 200 OK、ドラフト漫画本のカバーを作成するフォームを取得(/ draft-comic-book/3)(フォーム:POST/draft-comic-books/3/covers) 。

POST/draft-comic-books/3/covers
cover_type = front
cover_data =(バイナリ)
=> 302検出、場所:/ draft-comic-books/3/covers、ドラフト漫画本の新しい表紙にリダイレクト(/ draft-comic-book/3/covers/1)。

GET/draft-comic-books/3/publish
=> 200 OK、ドラフト漫画本を発行するためのフォームを取得します(id:3)(フォーム:POST/published-comic-books)。

POST/published-comic-books
title = foo
author = boo
publisher = goo
published = 2011-01-01
cover_type = front
cover_data =(バイナリ)
=> 302発見、場所:/ comic-books/3、カバー付きの公開された漫画本(id:3)へのリダイレクト。

62
Alex

カバーをリソースとして扱うことは、REST、特にHATEOASの精神です。したがって、GEThttp://example.com/comic-books/1にリクエストすると、カバー1のURIセットを含むプロパティを持つ本1の表現が得られます。ここまでは順調ですね。

あなたの質問は、漫画本の作成に対処する方法です。ブックに以上カバーがあるというビジネスルールがある場合、問題はありません。

POST http://example.com/comic-books

カバーレスのコミックブックデータを使用すると、新しいコミックブックが作成され、サーバーで生成されたIDが返されます(8として戻ってきたとしましょう)。これで、次のようにカバーを追加できます。

POST http://example.com/comic-books/8/covers

エンティティ本体にカバーがあります。

これで、ビジネスルールで常に少なくとも1つのカバーが必要だと言われた場合にどうなるかという、良い質問があります。いくつかの選択肢がありますが、最初に質問で特定したものは次のとおりです。

  1. 最初にカバーの作成を強制し、今では本質的に非依存リソースをカバーするか、コミックブックを作成するPOSTのエンティティ本体に最初のカバーを配置します。あなたが言うように、これはあなたがPOSTを作成する表現があなたがGETする表現と異なることを意味します。

  2. プライマリ、初期、優先、またはその他の方法で指定されたカバーの概念を定義します。これはおそらくモデリングハックであり、それを行うと、オブジェクトモデル(概念モデルまたはビジネスモデル)を微調整してテクノロジーに適合させるようなものになります。素晴らしいアイデアではありません。

これら2つの選択肢と、単にカバーレスコミックを許可することを比較検討する必要があります。

3つの選択肢のうち、どれを選択する必要がありますか?あなたの状況についてあまり知りませんが、一般的な1..Nに依存するリソースの質問に答えます。

  • RESTfulサービスレイヤーに0..Nを使用できる場合は、素晴らしいです。おそらく、RESTful SOA間のレイヤーは、少なくとも1つが必要な場合、さらなるビジネス上の制約を処理できます。 (どのように見えるかはわかりませんが、探索する価値があるかもしれません。..いずれにしても、エンドユーザーには通常SOAが表示されません。)

  • 1..N制約を単純にモデル化する必要がある場合は、表紙が共有可能なリソースであるかどうか、言い換えると、漫画本以外のものに存在する可能性があるかどうかを自問してください。現在、それらは依存リソースではないため、最初にそれらを作成し、コミック本を作成するPOSTでURIを提供できます。

  • 1..Nが必要で、カバーが依存している場合は、POSTの表現を維持するために本能を緩和するか、GETを同じにするか、同じにします。

最後の項目は次のように説明されます。

<comic-book>
  <name>...</name>
  <edition>...</edition>
  <cover-image>...BASE64...</cover-image>
  <cover-image>...BASE64...</cover-image>
  <cover>...URI...</cover>
  <cover>...URI...</cover>
</comic-book>

POSTを使用すると、既存のURI(他の書籍から借用)があれば許可しますが、1つ以上の初期画像も挿入します。書籍を作成していて、エンティティに初期のカバー画像がない場合は、409または同様の応答を返します。 GETでは、URIを返すことができます。

したがって、基本的にはPOSTとGETの表現を「同じにする」ことを許可しますが、GETのカバーイメージもPOSTのカバーイメージも「使用しない」だけです。それが理にかなっていることを願っています。

43
Ray Toal