186

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

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

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

とりあえず、コミックと、base64 で処理された画像を含むすべてのカバーを返すことができましGETた。ComicBookそれはおそらく、コミックを 1 冊入手するのに大したことではありません。しかし、システム内のすべてのコミックをテーブルに一覧表示するクライアント アプリを構築しているとします。
テーブルにはリソースのいくつかのプロパティが含まれますが、ComicBookすべてのカバーをテーブルに表示する必要はありません。それぞれが複数の表紙を持つ 1000 冊の漫画本を返すと、途方もなく大量のデータが送信され、その場合、エンド ユーザーにとって必要のないデータになります。

私の本能はCover、リソースを作成し、ComicBookカバーを含めることです。だから今CoverはURIです。GETコミックブック作品では、巨大なCoverリソースの代わりに、各カバーの URI を送り返し、クライアントは必要に応じてカバー リソースを取得できます。

現在、新しい漫画の作成に問題があります。確かに、 を作成するときに少なくとも 1 つのカバーを作成したいと思うComicでしょう。実際、それはおそらくビジネス ルールです。最初に を送信し、そのカバーの URI を取得してから、リスト内のその URI を使用して を実行するか、吐き出されるものとは異なる外観のリソースを取得
することで、クライアントにビジネス ルールを強制するように強制します。アウト。およびの着信リソースはディープ コピーであり、発信には依存リソースへの参照が含まれます。CoverPOSTComicBookPOSTComicBookPOSTGETGET

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

4

2 に答える 2

68

@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, コミック (id: 0) を表紙 (/covers/1, /covers/2) で取得します。

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 Found, Location: /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) (form: POST /draft-comic-books/3/カバー)。

POST /draft-comic-books/3/covers
cover_type=front
cover_data=(binary)
=> 302 Found, Location: /draft-comic-books/3/covers, ドラフト漫画の新しい表紙にリダイレクト (/draft-comic -book/3/covers/1)。

GET /draft-comic-books/3/publish
=> 200 OK, 下書き漫画を公開するためのフォームを取得 (id: 3) (form: POST /published-comic-books).

POST /published-comic-books
title=foo
author=boo
publisher=goo
published=2011-01-01
cover_type=front
cover_data=(binary)
=> 302 Found, Location: /comic-books/3, 出版された漫画本にリダイレクト(id: 3) カバー付き。

于 2011-09-21T13:18:05.437 に答える
49

カバーをリソースとして扱うことは、間違いなく REST、特に HATEOAS の精神に基づいています。そうです、GETへのリクエストhttp://example.com/comic-books/1は、表紙の一連の URI を含むプロパティを使用して、書籍 1 の表現を提供します。ここまでは順調ですね。

あなたの質問は、漫画本の作成をどのように処理するかです。本の表紙が0 個以上あるというビジネス ルールの場合、問題はありません。

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

with coverless comic book data は、新しいコミック本を作成し、サーバーが生成した 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 でカバーしないことを選択するだけです。それが理にかなっていることを願っています。

于 2011-08-18T09:09:20.140 に答える