web-dev-qa-db-ja.com

設計のトラブルREST長時間実行ジョブと部分更新の両方を使用するAPI

REST apiに設計の問題があります。ここで、リソースDeviceがあり、たまたまIoTデバイスを表しますが、PATCHを使用して部分的に更新できますが、更新されるものの長期実行ジョブを開始します。

例えば、

これがDeviceクラスです:

public class Device
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Ip { get; set; }
    public string Foo {get; set; }
}

次のようにPATCHリクエストを実行できます。

PATCH /api/devices/1234
{
    name: "abc123",
    Foo: "hello"
}

これにより、デバイスの名前とバーの両方のプロパティが更新されます。

ただし、次のように部分更新を使用してIpフィールドを更新する場合:

PATCH /api/devices/1234
{
    name: "abc123",
    ip: "1.2.3.4"
}

問題は、デバイスのIPを更新すると、物理IoTデバイスへのAPI呼び出しがトリガーされてIPが設定され、デバイスが再起動することです。

通常、長時間実行されるジョブの場合、記述されているものと同様のことを行います here 、操作の追跡ができるように、リソースのURLで受け入れられた202を返します。

これを部分更新と組み合わせて行うのは良い考えですか?私のパッチでipフィールドが指定されているとしましょう。このようなことをするのは理にかなっていますか:

PATCH /api/devices/1234
{
    name: "abc123",
    ip: "1.2.3.4"
}
Response:
{
    location: "/api/tasks/123"
}

このようにリクエストを追跡して、デバイスが正常に再起動したことをクライアントが知ることができるようにします...しかし、APIが時々このようにしか応答しないのは不便に感じます。実行時間の長いジョブをトリガーするプロパティを指定した場合のみ...ここで最適な設計の選択肢がわからない。

どんなアドバイスでも大歓迎です。

3
JSarwer

RESTful Casuistry は再起動についてかなり良い議論をしています。まだ慣れていない場合は、ぜひご覧ください。

通常、長時間実行されるジョブの場合、ここで説明されているのと同じようなことを行い、リソースのURLとともに受け入れられた202を返すので、操作を追跡できます。

これを部分更新と組み合わせて行うのは良い考えですか?

はい。

しかし、APIがこのように時々しか応答しないのは不便です。

ええと、要求が受け入れられたことを常に報告することで、必ずしも間違ったことはありません。

これを処理する最も簡単な方法は、最初にメッセージについて考え、その後メタデータについて心配することです。

HTTPでは、 安全でない操作 への応答は一般に アクションの結果 を説明するメッセージです。 Webサイトがフォームを送信することを想像すると、「操作は成功しました。このリンクをクリックして結果を確認してください」というページが表示されるか、「操作が受け入れられました。最新のステータス」。

同じ考えが別の方法で表現されました。統合プロトコルをナビゲートしています。場合によっては、アプリケーションが終了状態に移行することもあれば、中間保留状態に移行することもあります。

中間の状態は、失敗した状態と本質的に異なるものではなく、送信した要求が拒否されたという事実を説明する応答が返されます。これについて、次のことができます。

HTTPステータスコードと応答ヘッダーは、応答ではありません応答ではありません。それらは、応答から持ち上げられたメタデータです。私たちが実際に行っていることは、(消費者をターゲットとする)応答からsemanticsを取得し、それらの一部をメタデータに持ち上げて、genericプロトコルに参加しているコンポーネントは、何が起こっているのかを大まかに理解しており、適切に動作できます。

しかし、APIが時々このようにしか応答しないのは不便です。

今日あるウェブサイトについて考えてみてください。リンクをクリックすると、期待するデータに移動することがあります。ただし、以前のライセンスの有効期限が切れているために、ログイン画面が表示される場合があります。

言い換えれば、私たちはこのようなことを常に行います;アプリケーションが移行する可能性のある複数の状態があり、サーバーが1つを選択し、クライアントは進行方法を把握する必要があります。

REST後者への回答はハイパーメディアです。つまり、クライアントはサーバーから返される可能性のあるリンクのセマンティクスを理解する必要があり、サーバーは適切なリンクで応答することを選択します、それらを区別するセマンティックアノテーションを含みます。

あなたの例では、ほとんどのユースケースですぐにデータが準備されているため、「操作の結果」の応答には、結果を表示する方法をクライアントに通知するリンクのみが含まれます

<a ref="http://example.org/viewResult" href="...">

しばらく時間がかかる再起動の場合、「操作応答の結果には、進行状況を確認する方法をクライアントに通知するリンクのみが含まれます

<a rel="http://example.org/checkProgress" href="...">

これについての別の考え方:リンクの存在はリソースが利用可能であることをクライアントに通知し、リンク関係はクライアントにリソースが何であるかを通知します、プロトコルは、その種類のリソースで実行できるアクション(サポートされているhttpメソッド、使用可能なメディアタイプなど)をクライアントに通知します。

Atom Publishing は、この方法で定義されたプロトコルの本当に良い例です。

1
VoiceOfUnreason

GUIDをすぐに作成し、ジョブを非同期で開始した後、応答で返します。

{
    "UpdateId": myguid,
    "Result": "Update Started"
}

次に、追加のメソッドがあります。

DeviceUpdateStatus

これは引数としてguidを取り、クライアントが更新が完了したかどうかを知ることができるようにクエリにすることができます。

2
TheCatWhisperer