21

私はに取り組んでおり、REST API階層リソースを処理する方法を理解しようとしています。

バックグラウンド

簡単な例から始めましょう。私のAPIには、ユーザーユーザープロファイルレビューがあります。

  • ユーザーにはユーザープロファイルが関連付けられている必要があります(ユーザープロファイルは1人のユーザーにのみ対応します)
  • ユーザーにはレビューが関連付けられている場合があります(レビューは1人のユーザーにのみ対応します)

ユーザーのリソース表現は次のようになります。

User: {
   "u1": "u1value", // User's attributes
   "u2": "u2value",
   ...
   "links": [{
       "rel": "profile",
       "href": "http://..." // URI of the profile resource
   }, {
       "rel": "review",
       "href": "http://..." // URI of the review resource
   }]
}

ユーザープロファイルのリソース表現は次のようになります。

UserProfile: {
   "p1": "p1value", // Profile attributes
   "p2": "p2value",
   ...
   "links": [{
       "rel": "owner",
       "href": "http://..." // URI of the user resource
   }]
}

レビューリソースの表現は次のようになります。

Review: {
   "r1": "r1value", // Review attributes
   "r2": "r2value",
   ...
   "links": [{
       "rel": "owner",
       "href": "http://..." // URI of the user resource
   }]
}

リソースURIは次のようになります。

  • http://api.example.com/users/{userid}:ユーザーリソースへのアクセス
  • http://api.example.com/users/{userid}/profile:ユーザーのプロファイルリソースへのアクセス
  • http://api.example.com/users/{userid}/review:ユーザーのレビューリソースへのアクセス

リソースの作成:ユーザーを作成する正しい方法は何ですか?

次に、新しいユーザーを作成します。

  1. POST http://api.example.com/users {"u1": "bar", "u2": "foo"}新しいユーザーID=42を取得します
  2. POST http://api.example.com/users/42/profile {"p1": "baz", "p2": "asd"}
  3. PUT http://api.example.com/users {"u1": "bar", "u2": "foo", links: [{"rel": "profile", "href": "http://api.example.com/users/42/profile"]}

私の懸念:

  • 何かが1と2または2と3の間で壊れた場合はどうなりますか?
  • 3)では、サーバーはhttp://api.example.com/users/42/profile内のリンクを自動的に更新して、正しい所有者を指すようにする必要がありますか?
  • リンクフィールドを更新することは、関係を作成するための適切な方法ですか?または、手順3)をスキップして、URI規則に従ってシステムに関係を推測させる必要がありますか?(私は、URIが不透明であると見なされるべきであるといくつかの本を読みました。)
4

4 に答える 4

17

あなたの懸念は適切に配置されており、問題のリストは正しいです。私が提案する場合、あなたのアプローチは、リレーショナル DB アプローチを使用して INSERT を実行し、次の INSERT に使用するシーケンスから PK を取得するなどと非常によく似ています。

サーバーに参照整合性を維持させる

観察として、元のスキームに従っている場合でも、ステップ 3 を完全に省略してください。ユーザー ドキュメントを取得したときに表示される の URIlinksは、プロファイル レコードの存在に基づいてサーバーによって生成される必要があります。

たとえば、リレーショナル バックエンドを使用している場合は、USERS から SELECT してユーザー レコードを取得します。次に、プロファイルから選択します。レコードがある場合は、戻りデータ構造を変更して参照を含めます。

ドキュメント全体を POST

提起された他の問題を解決する一般的な方法は、ドキュメント全体をユーザー URL に投稿できるようにすることです( MongoDBなどの NoSQL データベースなど)。ここで、ドキュメントはユーザープロファイルです。

{ 
   "u1": "bar",
   "u2": "foo",
   "profile": {  
                 "p1": "baz",
                 "p2": "asd"
              }
}

このアプローチでは、サーバー上のエンドポイントがネストされた構造 (ドキュメント) を受け取り、INSERT を USERS に実行し、PK を取得してから、この PK を使用して PROFILES に INSERT を実行します。サーバー側でこれを行うと、いくつかの問題が解決されます。

  1. トランザクションはアトミックにすることができます
  2. クライアントとサーバー間のネットワーク交換は 1 つだけです。
  3. サーバーは面倒な作業を行い、トランザクション全体を検証できます (たとえば、プロファイルが有効でない場合、ユーザーは作成されません)。
  4. 手順 3 は必要ありません。

このアプローチは、上記で詳述した API に追加されることに注意してください。それでも、ユーザーのプロファイルに直接アクセスできるようにする必要があります。

GET - クライアントはフィールドを指定できます

老舗企業のAPIと比べてみると面白い。たとえば、LinkedInを見てみましょう。開発者 APIでは、ユーザーのデフォルトの GET は、ユーザーの名前、見出し、および URI のみを返します。

ただし、リクエストで追加のフィールドが指定されている場合は、ネストされたデータを取得できます。たとえば、http://developer.linkedin.com/documents/understanding-field-selectorsの 2 番目の例では、ユーザーの名前と役職のリストの会社名が返されます。彼らは開催しました。プロファイルとレビューにも同様のスキームを実装できます。

ドキュメント プロパティを更新するためのパッチ

挿入とクエリが邪魔にならないので、データを更新 (PATCH) する方法を検討する価値があるかもしれません。フィールドの上書きは明らかなので、たとえば次のようにhttp://api.example.com/users/42にパッチを適用できます。

{ 
   "u1": null,
   "u2": "new-foo",
   "profile": {  "p1": "new-baz"}
}

これはu1、設定を解除し、に設定u2し、プロファイルをnew-fooに更新します。フィールドが欠落している場合 ( )、フィールドは変更されないことに注意してください。この回答で説明されているように、PATCH は古い PUT よりも望ましいです。p1new-bazp2

プロファイルの更新のみが必要な場合は、新しいプロファイル レコードを直接http://api.example.com/users/42/profileにパッチします。

DELETE はカスケードする必要があります

最後に、削除するリソース (ユーザー、プロファイル、またはレビュー) を指す DELETE メソッドを使用して、削除を行うことができます。ユーザーを削除するとそのユーザーのプロフィールとレビューが削除されるように、カスケード削除を実装します。

于 2013-04-02T02:02:33.117 に答える
2

HATEOASに固執し、応答で取得した URL を逆参照する必要があります。

簡単にアクセスできるように、withUser.profileの が含まれているとします。hreflinkrel == profile

ユーザーを作成する

あなたが説明したPOSTを使用すると...しかし、IDではなく、リンクを備えたユーザーを返す必要があります。

User: {
   "u1": "bar", // User's attributes
   "u2": "foo",
   ...
   "profile": "http://api.example.com/users/42/profile",
   "links": [{
       "rel": "profile",
       "href": "http://api.example.com/users/42/profile"
   },
   ...
   ]
}

この時点で、User.profile のプロファイル リソース ( http://api.example.com/users/42/profile、または将来移行先の場所になる可能性があります) は、既定のプロファイルと同じになります。たとえば、空のドキュメントや、所有者のリンクだけが入力された状態などです。

プロフィールを更新する

profile = GET User.profile
profile.p1 = "baz"
profile.p2 = "asd"
PUT profile to the same url you just dereferenced

応答から取得した ID で URL を構築する代わりに、ドキュメントの href を逆参照することにより、API が変更されたときにクライアントを変更する必要がなくなります。次の場合のように - プロファイルはhttp://profiles.cdn.example.com/に移動されます - プロファイルは p3 値を取得します

「古い」API クライアントは、コードを変更しなくても引き続き機能します。

于 2013-04-08T10:49:32.447 に答える
1

実際には、成功したステップ (1) から、ID 番号だけでなく、HTTP コード 201 Created と、新しく作成されたリソースへのアドレス (URL) を取得する必要があります。手順 (2) が失敗した場合、REST API は、問題がクライアントにあるのかどうかを示す必要があります。たとえば、ドキュメントの形式が正しくない (発行コード 4xx) またはサーバー (5xx) などです。たとえば、その間にリソース 42 が削除された場合、コード 404 Not Found が返されます。

そこにステートレス REST API の問題があります。それらは、複数のリクエストで構成されるトランザクションをサポートできません。これを可能にするには、サーバー上でセッション (状態) を維持する必要があります。

ちなみに、あなたの例のステップ (3) の URL は、すべてのユーザーを置き換えていることを示唆しており、おそらくhttp://api.example.com/users/42を読む必要があります。

完全なユーザー + プロファイル ドキュメントを一度に送信して、1 つのアトミック トランザクションで 2 つのデータベース レコードに分割するか、部分的なユーザー データ (つまり、プロファイルのないユーザー) の永続性を許可するかを選択できます。

選択はコンテキストによって異なります。たとえば、ユーザーがプロファイルを持っていなくてもまったく問題ない場合があります (そのため、ユーザーがプロファイルを提供できます)。逆に、どのユーザーにも属さないプロファイル レコードを持つことは、おそらく受け入れられません。このロジックの適用に関する議論は、質問の範囲を超えており、選択した永続ストア (データベース) のタイプによって異なります。リレーショナル データベースは、外部キーを使用してこれを強制します。

于 2013-04-04T06:56:56.927 に答える