RESTful API。作成/更新されたオブジェクトを返す必要がありますか?


36

WebApiを使用してRESTful Webサービスを設計していますが、オブジェクトの更新/作成時にどのHTTP応答と応答本文を返すのか疑問に思っていました。

たとえば、POSTメソッドを使用してJSONをWebサービスに送信し、オブジェクトを作成できます。HTTPステータスをcreated(201)またはok(200)に設定し、「New Employee added」などのメッセージを返すか、元々送信されたオブジェクトを返すのがベストプラクティスですか?

同じことがPUTメソッドにも当てはまります。どのHTTPステータスを使用するのが最適で、作成されたオブジェクトまたはメッセージだけを返す必要がありますか?とにかくユーザーが作成/更新しようとしているオブジェクトを知っているという事実を考慮してください。

何かご意見は?

例:

新しい従業員を追加:

POST /api/employee HTTP/1.1
Host: localhost:8000
Content-Type: application/json

{
    "Employee": {
        "Name" : "Joe Bloggs",
        "Department" : "Finance"
    }
}

既存の従業員を更新します。

PUT /api/employee HTTP/1.1
Host: localhost:8000
Content-Type: application/json

{
    "Employee": {
        "Id" : 1
        "Name" : "Joe Bloggs",
        "Department" : "IT"
    }
}

反応:

オブジェクトが作成/更新された応答

HTTP/1.1 201 Created
Content-Length: 39
Content-Type: application/json; charset=utf-8
Date: Mon, 28 Mar 2016 14:32:39 GMT

{
    "Employee": {
        "Id" : 1
        "Name" : "Joe Bloggs",
        "Department" : "IT"
    }
}

メッセージのみでの応答:

HTTP/1.1 200 OK
Content-Length: 39
Content-Type: application/json; charset=utf-8
Date: Mon, 28 Mar 2016 14:32:39 GMT

{
    "Message": "Employee updated"
}

ステータスコードのみの応答:

HTTP/1.1 204 No Content
Content-Length: 39
Date: Mon, 28 Mar 2016 14:32:39 GMT

2
良い質問が、用語「ベストプラクティス」を使用するには、並べ替え、このサイト上のタブーであるmeta.programmers.stackexchange.com/questions/2442/...あなただけの再ワード質問したい場合があります。meta.programmers.stackexchange.com/questions/6967/…-
スヌープ

3
ちょっとしたフォローアップとして、リクエストにフラグを付けて(たとえば)モバイルアプリケーションがWiFiでオブジェクト全体を取得し、セルラーデータを使用する場合はIDのみを取得できるようにすることをお勧めしますか?JSONの汚染を回避するために使用するヘッダーはありますか?
アンドリューは

@AndrewPiliser興味深いアイデアですが、個人的には、1つのアプローチを選んでそれに固執するのが最善だと思います。その後、アプリケーションの開発や人気の高まりに
応じて

@AndrewPiliserのアイデアはUPDATE/INSERT ... RETURNING、SQL用のPostgresql バリアントに非常に似ています。特に、新しいデータの送信と更新されたバージョンの要求をアトミックに保持するため、非常に便利です。
beldaz

回答:


31

ほとんどのものと同様に、それは依存します。トレードオフは、使いやすさとネットワークサイズです。クライアントが作成されたリソースを見ると非常に役立ちます。最終作成時刻など、サーバーによって入力されたフィールドが含まれる場合があります。idを使用する代わりにを含むように見えるのでhateoas、クライアントはおそらくたった今POST編集したリソースのidを見たいと思うでしょう。

作成したリソースを含めない場合、任意のメッセージを作成しないでください。2xxおよびLocationフィールドは、リクエストが適切に処理されたことをクライアントが確信するのに十分な情報です。


+1クライアントがURIを作成しないようにするというhateoasの目的は、クライアントが特定のIDでサーバーが提供するURLテンプレートに入力できるようにすることによっても達成できます。はい、クライアントは「構成」しますが、「空白を埋める」という方法でのみです。純粋なHATEOASではありませんが、目的を達成し、(大きな)数の「アクション」uriを持つオブジェクトでの作業を、帯域幅の影響を少し少なくします。
マルジャンヴェネ

3
「任意のメッセージを作成しないでください」というアドバイスの+1
HairOfTheDog

「任意のメッセージなし」は、文字列メッセージまたは作成されたリソースではない戻り値に焦点を合わせていますか?私たちは作成したリソース(ただし、リソース自体)のIDを返す例に焦点を当てたことだし、どここのフィットがで思っていた。
Flater

12

個人的に、私は常に戻るだけ200 OKです。

質問を引用するには

とにかくユーザーが作成/更新しようとしているオブジェクトを知っているという事実を考慮してください。

クライアントに既に知っていることを伝えるために、追加の帯域幅を追加する必要があるのはなぜですか?


1
これは無効だ場合、私は考えていたものだ、あなたが検証メッセージを返すことができますが、それが有効だと作成された/更新された場合、その後のHTTPステータスコードをチェックし、ユーザーがそれに基づいてメッセージ例えば「万歳」と表示
iswinky

3
/ に関してstackoverflow.com/a/827045/290182を参照して、jQueryなどの混乱を避けてください。200204 No Content
beldaz

10

@iswinky POSTとPUTの両方の場合、常にペイロードを送り返します。

POSTの場合、内部IDまたはUUIDでエンティティを作成できます。したがって、ペイロードを送り返すことは理にかなっています。

同様に、PUTの場合、ユーザーの一部のフィールド(たとえば、不変の値)を無視する場合があります。また、PATCHの場合、データは他のユーザーによっても変更されている可能性があります。

永続化されたデータを送り返すことは常に良い考えであり、間違いなく害はありません。呼び出し元がこの返されたデータを必要としない場合、彼/彼女はそれを処理しませんが、statusCodeを消費します。それ以外の場合は、このデータをUIを更新するものとして使用できます。

DELETEの場合のみ、ペイロードを返送せず、応答コンテンツで200を実行するか、応答コンテンツで204を実行します。

編集:以下からのいくつかのコメントのおかげで、私は私の答えを言い換えています。私はまだAPIを設計して応答を送信する方法を待機していますが、設計思想の一部を限定することは理にかなっていると思います。

a)ペイロードを送り返すと言うとき、実際には、リクエストで送信された同じペイロードではなく、リソースのデータを送り返すことを意味していました。例:ペイロードの作成を送信する場合、バックエンドでUUIDや(多分)タイムスタンプ、さらには(グラフ)接続などの他のエンティティを作成できます。私はこのすべてを応答で送り返します(要求ペイロードだけではなく-これは無意味です)。

b)ペイロードが非常に大きい場合、応答を返送しません。これはコメントで説明しましたが、警告したいのは、非常に大きなペイロードを必要としないようにAPIまたはリソースを設計するために最善を尽くすことです。各リソースが15〜20のJSON属性で定義され、大きくはならないように、リソースをより小さく管理可能なエンティティに分割しようとします。

オブジェクトが非常に大きい場合、または親オブジェクトが更新されている場合は、ネストされた構造をhrefとして送り返します。

結論としては、消費者/ UIがすぐに処理し、UIを更新するために2〜5個のAPIを取得するのではなく、アトミックAPIアクションで実行するのに意味のあるデータを確実に返送しようとしますデータの作成/更新。

サーバー間APIは、これについて異なる考え方をするかもしれません。ユーザーエクスペリエンスを促進するAPIに焦点を当てています。


ペイロードが大きい場合、ペイロード全体を送り返すことが悪い考えである多くの状況を見ることができます。
beldaz

2
@beldazは完全に同意します。REST APIの設計に基づくYMMV。私は通常、非常に大きなオブジェクトを避け、一連のサブリソース/ PUTに分解します。ペイロードが非常に大きい場合、これを行うより良い方法があります。そこで、オブジェクト自体ではなくオブジェクトへの参照を返すHATEOAS(上記のMarjanのように)を実行したいと思うでしょう。
ksprashu

@ksprashu:「ペイロードを送り返すことは理にかなっている」-リソースはさまざまな方法で取得できるため、これは悪いアイデアだと思う:GET、POSTの応答、PUTの応答。これは、クライアントが、潜在的に異なる構造を持つ3つのリソースを取得することを意味します。ボディなしでURI(場所)のみを返す場合、リソースを取得する唯一の方法はGETです。これにより、クライアントは常に一貫した応答を取得できます。
mentallurg

@mentallurg私はこの権利を表明していないかもしれないことに気付きます。それを指摘してくれてありがとう。回答を編集しました。これがもっと理にかなっているかどうか教えてください。
ksprashu

自宅の仕事にサービスを実装する限り、それは実際には問題ではありません。好きなようにやってください。時間を節約してください。
mentallurg

9

リンクRFC規格を参照して、Postを使用して要求リソースを正常に保存すると、201(created)ステータスを返す必要があります。ほとんどのアプリケーションでは、リソースのIDはサーバー自体によって生成されるため、作成されたリソースのIDを返すことをお勧めします。オブジェクト全体を返すことは、Post要求のオーバーヘッドです。理想的な方法は、新しく作成されたリソースのURLの場所返すことです。

たとえば、従業員オブジェクトをデータベースに保存し、新しく作成されたリソースオブジェクトのURLを応答として返す次の例を参照できます。

@RequestMapping(path = "/employees", method = RequestMethod.POST)
public ResponseEntity<Object> saveEmployee(@RequestBody Employee employee) {
        int id = employeeService.saveEmployee(employee);
        URI uri = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").buildAndExpand(id).toUri();
        return ResponseEntity.created(uri).build();
}

この残りのエンドポイントは、応答を次のように返します。

ステータス201-作成済み

ヘッダーの場所→ http:// localhost:8080 / employees / 1



0

HTTPのパラメーターを条件に、リターン本文のペイロードを作成します。

多くの場合、不必要な往復を避けるために、何らかのコンテンツをAPIコンシューマーに返すことが最善です(GraphQLが存在する理由の1つ)。

実際、アプリケーションのデータ集約と分散が進むにつれて、このガイドラインを遵守してみます。

私のガイドライン

POSTまたはPUTの直後にGETを要求するユースケースがある場合は常に、POST / PUT応答で単に何かを返すのが最善の場合です。

これがどのように行われ、どのタイプのコンテンツがPUTまたはPOSTから返されるかは、アプリケーション固有です。ここで、アプリケーションが応答本文の「コンテンツ」のタイプをパラメーター化できる場合は興味深いでしょう(新しいオブジェクト、フィールドの一部、またはオブジェクト全体などの場所だけが必要ですか)

アプリケーションは、POST / PUTが受け取ることができる一連のパラメーターを定義して、応答本文で返す「コンテンツ」のタイプを制御できます。または、何らかの種類のGraphQLクエリをパラメーターとしてエンコードすることもできます(これは便利ですが、メンテナンスの悪夢にもなります)。

どちらにしても、私には次のように思われます:

  1. POST / PUT応答本文で何かを返すことは問題ありません(そして、ほとんどの場合望ましい)。
  2. これを行う方法はアプリケーション固有であり、一般化することはほとんど不可能です。
  3. デフォルトでは大きなサイズの「コンテキスト」を返したくない(GET-POST-followed-by-GETsから離れる理由全体を無効にするトラフィックノイズ)。

したがって、1)実行しますが、2)シンプルにします。

私が見た別のオプションは、代替エンドポイントを作成する人々です(たとえば、本文に何も返さないPOST / PUTの/ customersと、/ customersにPOST / PUTの/ customer_with_detailsを返しますが、応答の本文には何かを返します)。

ただし、このアプローチは避けます。合法的に異なるタイプのコンテンツを返す必要がある場合はどうなりますか?コンテンツタイプごとに1つのエンドポイント?スケーラブルではなく、保守もできません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.