135

JSON ドキュメントのコレクションに直接 REST インターフェイスを公開することに興味があります ( CouchDBまたはPersevereを考えてください)。私が直面している問題はGET、コレクションが大きい場合にコレクション ルートで操作を処理する方法です。

Questions例として、各行がドキュメントとして公開されているStackOverflow のテーブルを公開しているふりをします (このようなテーブルが必ずしも存在するわけではありません。「ドキュメント」のかなりのコレクションの具体的な例です)。コレクションは、通常の CRUD api で利用可能に/db/questionsなりGET /db/questions/XXXます。コレクション全体を取得する標準的な方法ですが、単純に各行を JSON オブジェクトとしてダンプすると、かなり大きなダウンロードが発生し、サーバー側で多くの作業が必要になります。PUT /db/questions/XXXPOST /db/questionsGET /db/questions

もちろん、解決策はページングです。Dojo は、独自の範囲単位でヘッダーを使用するという巧妙な RFC2616 準拠の拡張により、JsonRestStoreこの問題を解決しました。結果は、要求された範囲のみを返す です。クエリ パラメータに対するこのアプローチの利点は、... クエリのクエリ文字列を残すことです (たとえば、またはそのようなもの、およびエンコードされたはい)。Rangeitems206 Partial ContentGET /db/questions/?score>200%3E

このアプローチは、私が望む動作を完全にカバーしています。問題は、RFC 2616が 206 応答で次のように指定していることです (強調は私のものです)。

要求には、目的の範囲を示すRange ヘッダー フィールド (セクション 14.35 ) が含まれている必要があり、要求を条件付きにするためにIf-Range ヘッダー フィールド (セクション 14.27 ) が含まれている場合があります。

これは、ヘッダーの標準的な使用法のコンテキストでは理にかなっていますが、単純なクライアント/ランダムな人々の探索を処理するために 206 応答をデフォルトにしたいので問題です。

私は解決策を探して RFC を詳細に調べましたが、私の解決策に不満があり、問題に対する SO の見解に興味があります。

私が持っていたアイデア:

  • ヘッダー200で返す!Content-Range- 私はこれが間違っているとは思いませんが、応答が部分的なコンテンツのみであることをより明確に示すことが望ましいと思います。
  • Return400 Range Required - 必要なヘッダーには特別な 400 応答コードがないため、デフォルトのエラーを使用して手動で読み取る必要があります。これにより、Web ブラウザー (または Resty などの他のクライアント) を介した探索もより困難になります。
  • クエリ パラメーターを使用する- 標準的なアプローチですが、Persevere 形式でクエリを実行できるようにしたいと考えています。これにより、クエリの名前空間が切断されます。
  • 戻るだけ206- ほとんどのクライアントはびっくりしないと思いますが、私はむしろ RFC の MUST に反対したくありません
  • スペックを拡張!Return266 Partial Content - 206 とまったく同じように動作しますが、Rangeヘッダーを含めてはならない要求への応答です。266 は衝突の問題に遭遇しないほど十分に高いと考えており、それは理にかなっていますが、これがタブーと見なされるかどうかは明確ではありません。

これはかなり一般的な問題だと思います。私や他の誰かが車輪を再発明しないように、事実上の方法でこれが行われることを望んでいます。

コレクションが大きい場合、HTTP 経由で完全なコレクションを公開する最良の方法は何ですか?

4

12 に答える 12

33

私はあなた方の何人かに本当に同意しません。私は、REST サービスのこの機能に何週間も取り組んできました。私がやったことは本当に簡単です。私のソリューションは、REST の人々がコレクションと呼ぶものに対してのみ意味があります。

クライアントは、コレクションのどの部分が必要かを示す「Range」ヘッダーを含めなければなりません。さもなければ、要求されたコレクションが大きすぎて 1 回のラウンドトリップで取得できない場合、413 REQUESTED ENTITY TOO LARGE エラーを処理する準備ができている必要があります。

サーバーは、リソースのどの部分が送信されたかを指定する Content-Range ヘッダーと、コレクションの現在のバージョンを識別する ETag ヘッダーを含む 206 PARTIAL CONTENT 応答を送信します。私は通常、Facebook のような ETag {last_modification_timestamp}-{resource_id} を使用します。コレクションの ETag は、それに含まれる最近変更されたリソースの ETag であると考えています。

コレクションの特定の部分を要求するには、クライアントは "Range" ヘッダーを使用し、以前に実行された要求から取得したコレクションの ETag を "If-Match" ヘッダーに入力して、同じコレクションの他の部分を取得する必要があります。したがって、サーバーは、要求された部分を送信する前に、コレクションが変更されていないことを確認できます。より新しいバージョンが存在する場合は、412 PRECONDITION FAILED 応答が返され、クライアントにコレクションを最初から取得するように勧めます。これは、現在要求されている部分の前または後に一部のリソースが追加または削除された可能性があることを意味する可能性があるため必要です。

ETag/If-Match を Last-Modified/If-Unmodified-Since と組み合わせて使用​​して、キャッシュを最適化します。ブラウザとプロキシは、キャッシュ アルゴリズムをどちらかまたは両方に依存している場合があります。

検索/フィルター クエリを含める場合を除き、URL はクリーンであるべきだと思います。考えてみれば、検索はコレクションの部分的なビューにすぎません。cars/search?q=BMW タイプの URL の代わりに、より多くの cars?manufacturer=BMW が表示されるはずです。

于 2009-06-23T09:25:14.410 に答える
24

私の直感では、HTTP 範囲拡張機能はあなたのユース ケースに合わせて設計されていないため、試してはいけません。部分的な応答は を意味206し、206クライアントが要求した場合にのみ送信する必要があります。

Atom での使用など、別のアプローチを検討することをお勧めします (設計による表現は部分的であり、 status200とページング リンクで返される可能性があります)。RFC 4287およびRFC 5005を参照してください。

于 2009-05-29T06:54:28.207 に答える
8

応答コードAccept-RangesContent-Ranges使用して戻ることもできます。200これら 2 つの応答ヘッダーは、応答コードが明示的に提供するのと同じ情報を推測するのに十分な情報を提供します。206

私はページネーションに使用し、単純に aに対して a をRange返すようにします。200GET

これは 100% RESTfulであり、ブラウジングが難しくなることはありません。

編集: これについてブログ記事を書きました: http://otac0n.com/blog/2012/11/21/range-header-i-choose-you.html

于 2012-11-14T06:09:35.547 に答える
5

回答のページが複数あり、コレクション全体を一度に提供したくない場合、複数の選択肢があるということですか?

へのリクエストでは、各ページへのアクセス方法を指定するヘッダーと、URL のリストを含む JSON オブジェクトまたは HTML ページを/db/questions返します。300 Multiple ChoicesLink

Link: <>; rel="http://paged.collection.example/relation/paged"
Link: <>; rel="http://paged.collection.example/relation/paged"
...

Link結果のページごとに 1 つのヘッダーがあり (空の文字列は現在の URL を意味し、URL は各ページで同じであり、異なる範囲でアクセスされるだけです)、関係は今後のLink仕様に従ってカスタムのものとして定義されます。 . この関係は、あなたの習慣266、またはあなたの違反を説明するでしょう206. いずれにせよ、すべての例でクライアントを理解する必要があるため、これらのヘッダーは機械可読バージョンです。

(「範囲」ルートに固執する場合、2xx説明したように、独自のリターンコードがここでの最良の動作になると思います。アプリケーションやそのような[「HTTPステータスコードは拡張可能です。 "]、そしてあなたには正当な理由があります。)

300 Multiple Choicesは、ユーザー エージェントが選択する方法をボディに提供する必要があるとも述べています。クライアントが理解している場合は、Linkヘッダーを使用する必要があります。ユーザーが手動で閲覧している場合、URL に基づいてその特定のページのレンダリングを処理できる特別な「ページ化された」ルート リソースへのリンクを含む HTML ページでしょうか? /humanpage/1/db/questionsまたはそのような恐ろしい何か?


AcceptRichard Levasseur の投稿へのコメントは、ヘッダー (セクション 14.1)という追加のオプションを思い起こさせます。oEmbed 仕様が発表されたとき、HTTP を使用して完全に行われなかった理由を疑問に思い、それらを使用する代替案を作成しました。

最初の単純な HTTP の300 Multiple Choices、ヘッダー、および HTML ページを保持しますが、範囲を使用するのではなく、新しいページング関係でヘッダーの使用を定義します。後続の HTTP リクエストは次のようになります。LinkGETAccept

GET /db/questions HTTP/1.1
Host: paged.collection.example
Accept: application/json;PagingSpec=1.0;page=1

ヘッダーを使用Acceptすると、受け入れ可能なコンテンツ タイプ (JSON リターン) と、そのタイプの拡張可能なパラメーター (ページ番号) を定義できます。私の oEmbed の記事 (ここにリンクすることはできません。プロフィールに記載します) のメモを参考にしてください。pageパラメーターの意味を再定義する必要がある場合に備えて、非常に明示的に仕様/関係バージョンを提供できます。将来。

于 2009-06-04T06:00:20.200 に答える
4

編集:

もう少し考えてみると、Range ヘッダーはページネーションに適していないことに同意する傾向があります。つまり、Range ヘッダーは、アプリケーションではなく、サーバーの応答を対象としています。100 メガバイトの結果を提供したが、サーバー (またはクライアント) が一度に 1 メガバイトしか処理できない場合、それが Range ヘッダーの目的です。

また、リソースのサブセットは独自のリソース (リレーショナル代数と同様) であるため、URL で表現する価値があるという意見もあります。

基本的に、ヘッダーの使用に関する元の回答 (以下) を撤回します。


多かれ少なかれ、あなた自身の質問に答えたと思います-コンテンツ範囲で200または206を返し、オプションでクエリパラメーターを使用します。ユーザー エージェントとコンテンツ タイプをスニッフィングし、それらに応じてクエリ パラメータをチェックします。それ以外の場合は、範囲ヘッダーが必要です。

本質的に相反する目標があります-人々にブラウザを使用して探索させる(カスタムヘッダーを簡単に許可しない)か、ヘッダーを設定できる特別なクライアントを使用するように強制します(探索させません)。

リクエストに応じて特別なクライアントを提供することもできます。単純なブラウザのように見える場合は、ページをレンダリングして必要なヘッダーを設定する小さな ajax アプリを送信します。

もちろん、この種のものに必要なすべての状態を URL に含めるべきかどうかについての議論もあります。ヘッダーを使用して範囲を指定することは、「落ち着かない」と見なされる場合があります。

余談ですが、サーバーが "Can-Specify: Header1, header2" ヘッダーで応答でき、Web ブラウザーが UI を表示して、ユーザーが必要に応じて値を入力できるようにするとよいでしょう。

于 2009-06-03T03:12:24.703 に答える
3

ここでの本当の問題は、仕様に 413 - Requested Entity Too Large に直面したときに自動リダイレクトを行う方法を示すものが何もないことだと思います。

私は最近、これと同じ問題に苦しんでおり、 RESTful Web サービスの本でインスピレーションを探しました。個人的には、ヘッダー要件のために 206 は適切ではないと思います。私の考えも 300 に導かれましたが、それはさまざまな MIME タイプのためのものだと思ったので、Richardson と Ruby がこの件について何を言わなければならなかったかを付録 B の 377 ページで調べました。基本的には 300 であるべきだという考えを無視して、200 で送り返します。

これはまた、atom から取得した次のリソースへのリンクの概念とも一致します。私が実装した解決策は、送り返していた json マップに「次へ」と「前へ」のキーを追加することでした。

後で、307 (/db/questions/1,25 のようなリンクへの一時的なリダイレクト) を送信して、元の URI を正規のリソース名として残すことを考え始めましたが、適切な名前の従属リソース。これは私が 413 から見たい動作ですが、307 は良い妥協点のようです。ただし、実際にはまだコードでこれを試していません。さらに良いのは、最近の質問の実際の ID を含む URL にリダイレクトすることです。たとえば、各質問に整数の ID があり、システムに 100 の質問があり、最新の 10 件を表示したい場合、/db/questions へのリクエストは /db/questions/100,91 に 307 する必要があります。

これは非常に良い質問です。質問していただきありがとうございます。あなたは、私が何日もかけてそれについて考えてきたことに気が狂っていないことを確認しました.

于 2009-06-04T21:11:24.727 に答える
3

Atom Feed Protocol のようなモデルの使用を検討することもできます。これは、コレクションの正常な HTTP モデルとその操作方法を備えているためです (異常とは WebDAV を意味します)。

コレクション モデルと REST 操作を定義するAtom Publishing Protocolがあり、 RFC 5005 - Feed Paging and Archivingを使用して大きなコレクションをページングできます。

Atom XML から JSON コンテンツに切り替えても、アイデアに影響はありません。

于 2009-06-03T02:10:40.787 に答える
2

範囲ヘッダーの大きな問題の 1 つは、多くの企業プロキシが範囲ヘッダーを除外することです。代わりにクエリ パラメータを使用することをお勧めします。

于 2014-08-15T13:17:35.293 に答える
1

ヘッダーを検出し、Range存在する場合は Dojo を模倣し、存在しない場合は Atom を模倣できます。これにより、ユースケースがきれいに分割されているように思えます。アプリケーションから REST クエリに応答している場合、Rangeヘッダーでフォーマットされていることが予想されます。カジュアルなブラウザーに応答している場合は、ページング リンクを返すと、ツールはコレクションを探索する簡単な方法を提供します。

于 2009-06-04T05:18:31.323 に答える
0

これを行う最善の方法は、範囲をクエリパラメーターとして含めることです。例: GET /db/questions/?date>mindate&date<maxdate . クエリ パラメータなしで /db/questions/ に GET すると、 場所: /db/questions/?query-parameters-to-retrieve-the-default-page で303 が返されます。次に、API を使用してコレクションに関する統計情報を取得する別の URL を提供します (たとえば、コレクション全体が必要な場合に使用するクエリ パラメーター)。

于 2009-06-05T19:43:46.527 に答える