REST APIを「正しい」方法で実行しなかった場合の結果?


8

私はこの方法でこの質問をします-私のREST APIを「正しい」方法で実装しないことのソフトウェアエンジニアリングの懸念は何ですか?

「正しい」方法とはどういう意味ですか?まあ、私が正しい方法についての私の認識を説明できるようにしてから、私がそれをどのように行っているかを説明します(また、JSON REST APIについて話していると仮定します)。

正しい方法

  1. 無国籍。これは私が得る部分です。クライアントは常に100%いつまでも状態を維持します。それはサーバーの仕事ではなく、クライアントの仕事です。

  2. 各動詞の予想されるアクションと応答:

    • GET-完全に指定されたリソースを取得します。リクエストの承認またはクエリパラメータのいずれかによってのみ制限されます。これにより、プロセス内のリソースが変更されることはありません
    • POST-リソースの説明全体(JSONオブジェクトなど)を指定すると、リソースを作成し、日付やIDなどのサーバー側のプロパティも作成してそのリソースを返します。
    • DELETE-指定されたリソースを削除し、応答として何らかの200 OKのみを与えます
    • PUT -指定されたオブジェクト全体宣言入力として、入力で与えられたフィールドのそれぞれにリソースのすべてのフィールドを更新し、特定の場所でリソースを更新します。明確にするために、これはオブジェクト全体が入力として渡されることを期待しています。更新されたリソース全体が、すべてのフィールドとともに(許可またはその他の入力フラグに従って)返されます。
    • PATCH-リソースの変更が必要なフィールドのみを指定して、入力として指定された指定されたリソースのフィールドのみを更新します。(これは私が不明瞭なところです):リソース全体が返されますか?(または、更新されたフィールドだけですか?Dunno。気にしないでください。)
  3. リソースパス。リソースの相互関係を考えると、リソースパスは次のいずれかになります。
    • / parentresource /:id
    • / parentresource /:id / childresource
    • / parentresource /:id / childresource /:childId
    • / parentresource /:id / childresource /:childId / subresource /:subresourceId(この例では、サブリソースは、親リソースに属する子リソースに属しています)。

やりたいこと

上記は、REST APIがどのように機能するかについての私の理解です。次に、上記のバリエーションのいくつかをリストします。

  1. PUT / PATCH-変更のためにリソース全体を渡すポイントは何ですか?リソースの変更にはPUTのみを使用し、更新するフィールドのみを渡します。その結果、パッチを使用する必要はありません
  2. リソースパス-アプリケーションでGUIDを使用しています。その結果、それらは世界的にユニークになります。単独でサブリソースを一意に参照できるのに、親リソースを含む完全なリソースパスが必要なのはなぜですか?以下のような:
     
    /サブリソース/:subresourceId
     
    :もし私のような完全なパスが必要となるサブリソースを参照しようとし、それを「正しい」やり方をやっていた
     
    / parentresource /:ID / childresource /:たchildID /サブリソース/:subresourceIdを
     
    すべてその必要?パスに、指定された:childIdが実際に所有していない:subresourceIdと、親:idが所有していない:childIdのディットが含まれている場合、追加のエラー処理が必要になるためです。私のサーバー側はリソースの許可を処理しています。フルパスではなく、リソース自体を参照することはできませんか?

  3. 戻り応答。たとえば、私のデータ構造が階層ツリーであり、ツリーの深さに実質的な制限がないとします。リソースは、ツリーのさまざまなレベルに階層的に配置されています。

    • GETは明白です。このツリー全体を取得した場合、リソース全体がリソース内に含まれた状態で、ツリー全体が応答として期待されます。
    • 新しいリソースを作成するためにPOST、更新するためにPUT、または削除するためにDELETEを実行する場合、作成/更新/削除したリソースを表示するだけでなく、ツリー内のデルタを表示したいと思います。POST、PUT、またはDELETEを実行するたびに、親ツリーのGETを再度呼び出す必要はありません。特に、ツリーにほとんど変更がなく、デルタのみを表示したい場合に使用します。

うまくいけば、私の質問は明確です。

私が説明したようにREST実装を見たとしたら、それを見て、ソフトウェアエンジニアリングの懸念事項について教えてください。もしそうなら、彼らは何でしょうか?


4
不思議なことに、RESTのアーキテクチャ上の制約は関係ありません。あなたの質問は、WEBおよびHTTPセマンティクスに厳密に結び付けられています。あなたはこれらの最後のものに満足していないように見えます、私はどう思いますか、なぜWebインターフェイスを作るのですか?一方、「正しい方法」や「間違った方法」といったものはありません。お客様のニーズに最適な実装だけです。あなたが「正しい」と呼ぶものは、それはより「ウェブフレンドリー」と「開発者フレンドリー」です。「Webフレンドリー」であるほど、WWWアーキテクチャのメリットが大きくなります。それがウェブの「こと」をするポイントです。
Laiv

RESTから逸脱するリスクは、RESTfulインターフェースを期待している誰かがWebアプリケーションの動作によって混乱することです。しかし、あなたが提案することは、完全に混乱させるほど大きく逸脱しているようにも見えないので、あなたが提案していることは完全に問題ないと主張します。ドキュメントを追加するだけです!
Neil

@Laiv、あなたは良い質問をします- なぜWebインターフェースを作るのですか?私は、ReactやVueなどのクライアント側のレンダリングフレームワークをサポートする他のアーキテクチャに精通していないので、必要なデータを送信するWebインターフェースを持っています。明確にするために、柔軟性とパフォーマンス上の理由から、サーバー側のレンダリングは必要ありません。私はGraphQLがオプションであることも知っていますが、私はそれについてひどく不慣れです。
Michael Plautz

GraphQLはアーキテクチャスタイルではなく、内部に多数のボイラープレートコードを備えたもう1つのクエリ言語です。RESTに行かなくてもWWWを利用できます。WebSocketsまたはProtocolBuffersはまだありません。アプリケーションが純粋に表現的な統計転送でない場合、RESTはサービスを提供しません。
ライヴ

回答:


3

ここでの基本的な答えは、技術的なレベルでアイデアが機能する可能性があるということですが、RESTの標準化された規則に準拠しているという意味ではありません。


  1. PUT / PATCH-変更のためにリソース全体を渡すポイントは何ですか?リソースの変更にはPUTのみを使用し、更新するフィールドのみを渡します。その結果、パッチを使用する必要はありません

あなたのアイデアは技術レベルで機能しますが、RESTがどのように説明されているかではありません。動作中のコード(つまり、コンパイルエラーや実行時エラーがないこと)に関する議論は、常に慣例の問題であり、必ずしも明確な技術的優位性があるとは限らないことに注意してください。


  1. リソースパス-アプリケーションでGUIDを使用しています。その結果、それらは世界的にユニークになります。単独でサブリソースを一意に参照できるのに、親リソースを含む完全なリソースパスが必要なのはなぜですか?

「子/親」エンティティを定義する方法には多くのニュアンスがあります。最も一般的には、1対多(親から子)の関係を指します。

ただし、RESTの場合、子を子にする理由の1つは、親を介してのみアクセスできるという期待があり、グローバルに一意の(外部的に既知の)識別子を持たないことです。
これは、ドメイン駆動型開発における集合体(およびそのルート)と同じ哲学(必ずしも同じ理由である必要はない)に従っていると思います。

DDDアグリゲートは、単一のユニットとして扱うことができるドメインオブジェクトのクラスターです。集合体は、そのコンポーネントオブジェクトの1つを集合体のルートにします。アグリゲートの外部からの参照は、アグリゲートのルートにのみ行く必要があります。

あなたの場合、あなたが「親」と呼ぶものは、集約ルートとして機能します。外部の発信者のための単一の連絡先(必要な場合)。

これから、子供は実際には別の集合体であると結論付けることができます。それは事実かもしれませんが、私はその決定について警告を発したいと思います。特定のタイプのフィールドに基づいてアーキテクチャを構築するべきではありません。すべてのエンティティに対して常にグローバルに一意のIDを使用し続けるかどうかを知る方法はありません。それが変更された場合、何らかの理由で、RESTアーキテクチャの実行可能性が損なわれます。子が一意に識別できなくなり、親を通じて参照する必要がある状況になる可能性があるためです。


  1. 新しいリソースを作成するためにPOST、更新するためにPUT、または削除するためにDELETEを実行する場合、作成/更新/削除したリソースを表示するだけでなく、ツリー内のデルタを表示したいと思います。

デザインの操作順序に違反しています。REST APIは、特に消費者に依存しないことを目的としています。APIは、その消費者の1人の仕様に従って構築されるべきではありません。

「ツリー内のデルタを表示したい」と言うと、実際に言っているのは「使用中のアプリケーションはツリー内のデルタを見るだけでよい」ということです。しかし、それはREST APIにとって重要ではありません。標準化されたアプローチを提供するだけです。

高度にカスタマイズ可能なツールが欠けていることがよくあり、代わりに最も一般的に使用されるツールを支持するのが標準化されたアプローチの性質です。


パスから逸脱できますか?まあ、それは技術レベルで動作します。しかし、それはもはや純粋なRESTではなくなります。これは非常に状況に応じたものであり、オプションを比較検討する必要があります。

  • 多くのさまざまな消費者に対応することが期待されるAPIを作成している場合は、できるだけRESTを使用することをお勧めします。
  • 自分で開発したコンシューマーを1つだけ持つAPIを構築する場合。そのため、純粋なRESTに固執する必要はありません。
  • 道から外れるとは、他の開発者がそれを理解できるように、どのように迷ったかを文書化する必要があることを意味します。純粋なRESTを使用する場合、ドキュメントを作成する必要はなく、他の開発者はカスタマイズされたアプローチを理解するために時間と労力を費やす必要はありません。

9

REST APIを「正しい」方法で実装しないことのソフトウェアエンジニアリングの懸念は何ですか?

主なものは、私の考えでは、特定のビジネスケースではなく、標準のみを知っている汎用コンポーネントに作業を委任できることです。

統一されたインターフェースを順守している場合、他の関係者が自分のコンポーネントとうまく統合するコンポーネントを構築する方が簡単です。

こちらが2008年のフィールディングの記事です

RESTは、複数の組織にまたがる長期間有効なネットワークベースのアプリケーションを対象としています。

「長期」を管理する方法の1つは、渡すメッセージのセマンティクスを説明する明確な標準を持つことです。誰もが何をPUT意味するかに同意する場合、それらのリクエストのコンシューマーとプロデューサーは独立して開発でき、2つの間の中間コンポーネントは、特定のコンテキストでメッセージの詳細を知る必要なく、賢明なアクションを実行できます。

PUT / PATCH-変更のためにリソース全体を渡すポイントは何ですか?リソースの変更にはPUTのみを使用し、更新するフィールドのみを渡します。その結果、パッチを使用する必要はありません

そのときの使用のポイントは何PUTですか?

PURPLE /014d8c83-604d-4cf0-a6ba-e1f7ef8c4898 HTTP/1.1
...

これは、HTTPメッセージの完全に有効なリクエスト行であり、セマンティクスの変更によって混乱することはありません。

同等に

POST /014d8c83-604d-4cf0-a6ba-e1f7ef8c4898 HTTP/1.1
...

どちらも意味の制約がないものです。サーバーは、その要求の処理を任意の方法で実装できます。

私がJSON REST APIについて話していると仮定します

JSONパッチJSONマージパッチの標準がすでに提案されているため、PATCHを使用することへの抵抗は特に奇妙です。パッチドキュメント形式の標準化作業はすでに行われている可能性があります。

別の有効な代替手段は、パッチドキュメントを別のリソースとして扱うことです。意味的に、あなたは次のようなものを想像するかもしれません

PUT /014d8c83-604d-4cf0-a6ba-e1f7ef8c4898/patches/5c42c414-03c0-4ac5-af14-2b1165ac98b3 HTTP/1.1

これにより、正直で統一されたメッセージセマンティクスが得られ、標準化されたキャッシュの無効化が犠牲になります。

コードレビューの設定では、PUTのセマンティクスを再定義しようとする提案された変更を拒否します。

HTTPは、GETの結果が安全であることを要求することを試みません。それが行うことは、操作のセマンティクスが安全であることを必要とするため、プロパティの損失を引き起こす結果として何かが発生した場合に、インターフェースまたはそのインターフェースのユーザーではなく、実装の障害です(お金、BTW、この定義のためにプロパティと見なされます)。- フィールディング、2002

同じことが同じように当てはまりPUTます。PUTの実装が標準化されたセマンティクスから逸脱している場合、その結果の損傷は実装に責任があります。

リソースパス-アプリケーションでGUIDを使用しています。その結果、それらは世界的にユニークになります。単独でサブリソースを一意に参照できるのに、親リソースを含む完全なリソースパスが必要なのはなぜですか?

まったく問題ありません。RESTは、リソース識別子にどのスペルを使用してもかまいません。

グーグルランディングページを検討してください。今日のDoodleのURIのスペルに注意を払う必要がありますか?または検索フォームが送信されている場所は?いいえ、もちろんです。HTMLペイロードにはURIが含まれており、クライアントは提供された識別子を標準的な方法で使用するだけで、それらの識別子を分析する必要はありません。

URIにエンコードされた情報は、それ自体の目的のために、オリジンサーバーの裁量にあります。

このようなURIをAPIのエントリポイントとして使用することはお勧めしません。 https://www.example.org/df8f5f87-15ff-4212-8fb8-4fbca2c7efcf人間の消費には少し厄介です。UUIDリソースにリダイレクトする人間が読めるURIは問題ありませんが、UUIDリソースのコンテンツを返す人間が読めるURIの方が優れています。

新しいリソースを作成するためにPOST、更新するためにPUT、または削除するためにDELETEした場合、作成/更新/削除したリソースを表示するだけでなく、ツリー内のデルタを表示したい

それは結構です-もう一度、標準を見てください。

200応答で送信されるペイロードは、要求メソッドによって異なります。この仕様で定義されているメソッドの場合、ペイロードの意図する意味は次のように要約できます。

アクションのステータスまたはアクションから取得した結果の表現をPOST

アクションのステータスの表現をPUT、DELETE

場合によっては、リソースの新しい表現を応答の一部として送信することが理にかなっています(クライアントにGET要求/応答の待ち時間を節約するため)。


2
What's the point in using PUT then? - 確かに。POSTは完璧に機能します。
Robert Harvey

@RobertHarveyまたは、なぜ使用しないのPATCHですか?定義はよりよく適合します。
ソロモンウッコ

@SolomonUcko:すべてをPOSTにすることには、シンプルさのメリットがあります。
Robert Harvey

3

RESTは、ステートレスな方法でリソースを操作するためのアーキテクチャスタイルです(ステートレスとは、各操作がそれ自体に依存し、実行された可能性のある他の操作に依存しないことを意味します)。

動詞PUT / PATCH / POST / GET / DELETEの使用は、リソースの転送と操作に使用されるHTTPプロトコルの一般的な使用から来ています。これらの動詞の意味は、インターネット標準(RFC7231)で定義されています。

その背景を考えると、PUTの使用は非標準であり、APIを使用したい他の開発者を混乱させる可能性があります。

リソースパスについては、正確なスペル(子リソースが子としてリストされている場合を含む)を気にする人はほとんどいません。人々が気にするのは、各リソースが一意に識別されることです。このシステム/parent/:pid/child/:cidは、子IDが1つの親内でのみ一意であり、リソースへのグローバルに一意のパスを保持している場合によく使用されます。


2
バートの答えに加えて。URIを読んで理解する必要があるのはHTTPクライアントだけであり、リソースの階層は関係ないことを思い出してください。したがって、URIが階層、関係、またはその他の種類の関係を表すかどうかは、それを読む必要のある貧しい生物にとってのみ意味があります
Laiv

1
  1. PUT / PATCH-変更のためにリソース全体を渡すポイントは何ですか?リソースの変更にはPUTのみを使用し、更新するフィールドのみを渡します。その結果、パッチを使用する必要はありません

ここでは実際にPATCHをセマンティクスとして使用する必要があるようです。

  • PUTは正確な望ましい状態を説明します。これは、リソースが頻繁に変更される可能性があり、特定のコンテキスト内で必要な変更を行う必要がある場合に役立ちます。

  • PATCHは、必要なデルタをそこにあるものに説明します。これは、変更がコンテキストを気にしない場合、または関連するコンテキストがリソース全体よりもはるかに小さい場合に役立ちます。

画像は、全体をアップロードするだけで意味がある場合の良い例です。関連するコンテキストを確保するために、リソース全体を伝達することが重要です。

逆に、音楽プレイリストの再生回数を更新することは、デルタである方が理にかなっている場合があります。その小さな変更のためではなく、リスト全体を再送信すると、リストのコンテンツに対する変更を簡単に取り消すことができるためです。

  1. リソースパス-アプリケーションでGUIDを使用しています。その結果、それらは世界的にユニークになります。単独でサブリソースを一意に参照できるのに、親リソースを含む完全なリソースパスが必要なのはなぜですか?

...をちょきちょきと切る...

それだけでいいの?パスに、指定された:childIdが実際に所有していない:subresourceIdと、親:idが所有していない:childIdのディットが含まれている場合、追加のエラー処理が必要になるためです。私のサーバー側はリソースの許可を処理しています。フルパスではなく、リソース自体を参照することはできませんか?

いいえ、パスを使用する必要はありません。すべてのファイルをデスクトップに保存するとしますか?番号?何故なの?

おそらく、見やすくするために何かをする必要があります。同じ問題がここにあります。GUIDは、これを設定、デバッグ、または実行しているときに、何を処理しているかを通知しません。

それでは、このシステムをサポートすることはどのように感じますか?またはそれと対話するために?あなたがそれについて考えていないなら、しばらく時間がかかり、あなたがラインを押し下げている仕事を考えてください。

明示的なパス情報があると、リクエストの検証に役立ちます。また、サポートやダウンストリームのシステム開発者がこれらのURLにアプローチして使用できるように、情報を十分に区別するのにも役立ちます。

  1. 戻り応答。たとえば、私のデータ構造が階層ツリーであり、ツリーの深さに実質的な制限がないとします。リソースは、ツリーのさまざまなレベルに階層的に配置されています。

    a。GETは明白です。このツリー全体を取得した場合、リソース全体がリソース内に含まれた状態で、ツリー全体が応答として期待されます。

あなたは深さの制限を課したいと思うかもしれません、それは単に、ある賢い子供が彼らが考えることができるすべての操作のためにあなたのサイトのルートを単に得ないようにするためです。

b。新しいリソースを作成するためにPOST、更新するためにPUT、または削除するためにDELETEを実行する場合、作成/更新/削除したリソースを表示するだけでなく、ツリー内のデルタを表示したいと思います。POST、PUT、またはDELETEを実行するたびに、親ツリーのGETを再度呼び出す必要はありません。特に、ツリーにほとんど変更がなく、デルタのみを表示したい場合に使用します。

リソースの更新が、その親に重要で予測不可能な方法で影響している場合は、他の問題があります...状態モデルを調べて、情報が飛び交っている理由を理解する必要があります。

デルタの項目別リストのみを返したい場合は、どうしてですか?さまざまなパラメーターによって切り替えられる複数の出力ビューをサポートしないのはなぜですか?Jenkinsは、XMLまたはjsonの選択でAPI応答を返し、必要なサブツリーを抽出するためのいくつかのフィルターを指定できます。

自分で使う

率直に言って、作成しているものから一歩下がって、それをサポートするか、別のアプリケーションを作成して使用します(既存のアプリケーションの1つではありません)。小さなバックグラウンドコンテキストが得られるように、サードパーティのAPIでも同様のことを行います。

サポートリクエストを直接解決していない、またはクライアントアプリケーションに直接必要ではない何かをしなければならない場合、APIは理想的ではなく、なぜそれが理想的ではないのかがわかります。修正するか、同じ間違いをしないようにしてください。

たとえば、特定のURLへのリクエストが常に失敗する場合、何が失敗しているのか、そしてその理由を特定するためにどれだけの労力を費やす必要がありますか?どのような手順を踏みましたか。URIを改善したり、ログを記録したり、監視を改善したりすることで、これらの手順の1つを回避できましたか?

同様に、新しいクライアントを作成する場合、ドキュメントまたはAPIのソースコードを何回参照する必要がありますか?その必要性を減らすために何ができますか?自分の期待に反するのをやめるために何ができますか?サーバーを維持するための悪夢にすることなく、クライアントアプリケーションの問題を単純化するために何ができますか?

正しい方法

率直に言って、正しい方法は状況に応じたものです。RESTは、いくつかの問題について、さまざまな状況で機能することが示されている一連のプラクティスです。問題が当てはまらない場合は、当てはまらないでください。ただし、これらの方法を使用すると主張しないでください。


-1

REST APIのほとんどの機能は理由がありますが、それはあなたに関連する理由ではないかもしれません。たとえば、PUTのようにリソース全体を提供することは、べき等性が必要な場合には適切ですが、そうでなければ必要ありません。(POSTまたはPATCHを代わりに使用して、エンドポイントがべき等ではないという事実をアドバタイズする方が、ユーザー/同僚/何に対しても良いと思いますが。)

パスについては、私は休息に関連していると聞いたことがありません。/root/345dd4dc-e175-455f-b545-85b1b1ce3e82と同じくらい木の一部です/foo/bar/baz。たぶんユーザーフレンドリーではないかもしれませんが、私が見る限りではそれほど落ち着きはありません。

RESTがそのまま設計された理由についてより詳細な推論が必要な場合は、元の論文を読んでみてくださいhttps : //www.ics.uci.edu/~fielding/pubs/dissertation/fielding_dissertation.pdf

RESTが会話で表現される方法や今日のAPIで使用される方法とはかなり異なることに気付くかもしれません。明らかに、他の多くの人々がそれから離れる理由を、良いか悪いかを見つけました。

私は特にあなたが関連しているかもしれないこの引用が好きです:

HTTPはトランスポートプロトコルとして設計されていません。これは、メッセージがWebアーキテクチャのセマンティクスを反映する転送プロトコルであり、それらのリソースの表現の転送および操作を通じてリソースにアクションを実行します。この非常にシンプルなインターフェースを使用して幅広い機能を実現することは可能ですが、HTTPセマンティクスが仲介者から見える状態を維持するには、インターフェースに従う必要があります。そのため、HTTPはファイアウォールを通過します。最近提案されたHTTPの拡張機能のほとんどは、WebDAV [60]を除いて、基本的に誤った考えであるファイアウォールを介して他のアプリケーションプロトコルを移動する方法として単にHTTPを使用しています。ファイアウォールを設置する目的を損なうだけでなく、ただし、ファイアウォールベンダーは追加のプロトコルフィルタリングを実行する必要があるだけなので、長期的には機能しません。したがって、HTTPの上でこれらの拡張を行うことは意味がありません。その状況でHTTPが実行するのは、レガシー構文からオーバーヘッドを追加することだけだからです。HTTPの真のアプリケーションは、プロトコルユーザーのアクションをHTTPセマンティクスを使用して表現できるものにマッピングし、アプリケーションの知識がなくてもエージェントや仲介者が理解できるサービスへのネットワークベースのAPIを作成します。


2
「代わりにPOSTまたはPATCHを使用して、エンドポイントがべき等ではないという事実を宣伝することは、ユーザー/同僚/何にとっても良いことだと思いますが。」–これは見栄えの問題ではありません。HTTPメソッドはセマンティクスを正確に定義しており、世界中のWebインフラストラクチャ全体がそれらのセマンティクスに依存しています。たとえば、プロキシが再試行することがありPUTますが、知らなくても、リクエストを複数回、およびHTTP仕様はそれが言うので、彼らはそれを行うことを許可されているPUT冪等ですPUTがべき等でない場合、これはサービスを壊します
イェルクWミッターク

2
しばらくの間、「Webアクセラレータ」が大流行し、実際、モバイルデバイスに復活しています。これらのアクセラレーターは、リソースのプリフェッチとキャッシュを行いましたが、HTTP仕様GETHEADは、純粋で副作用のないことが保証されているため、そうすることが許可されていました。一部の人々は、デザインが不適切なWebアプリを使用していて、コンテンツの削除がGETリクエストで行われ、WebアクセラレーターGETが見つけたすべてのURIにリクエストを送信して、データを失いました。
イェルクWミッターク

もちろんです。私は、適切に機能しているWebに関する熱意をこれ以上高めることができません。それは私にとって失われた戦いのように感じますが、多分あなたはこれらのようなものが実際に機能するより良い環境にいるでしょう。私がhttpである場所は、netcatの代わりにブラウザからデバッグ可能であることが追加されたトランスポートにすぎません。
モノセル
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.