Problemi nella progettazione dell'API REST che utilizza sia lavori di lunga durata che aggiornamenti parziali

3

Ho un problema di progettazione nel mio REST API, dove ho una risorsa Device , che capita di rappresentare un dispositivo IoT, può essere parzialmente aggiornato usando PATCH, ma alcune delle cose che vengono aggiornate inizieranno un lavoro di lunga durata.

Ad esempio,

Ecco il dispositivo di classe:

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

Posso fare una richiesta PATCH in questo modo:

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

che aggiornerà sia il nome & Proprietà della barra del dispositivo.

Tuttavia, se voglio aggiornare il campo Ip usando l'aggiornamento parziale in questo modo:

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

Il problema è che l'aggiornamento di ip on Device attiva una chiamata API al dispositivo IoT fisico per impostare l'ip, che provoca il riavvio del dispositivo.

Solitamente per lavori di lunga durata, farei qualcosa di simile a ciò che è descritto qui e restituisce 202 accettato con l'URL della risorsa in modo da poter tracciare l'operazione.

È una buona idea farlo in combinazione con un aggiornamento parziale? Supponiamo che il campo ip sia specificato nella mia patch, ha senso fare qualcosa del genere:

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

In questo modo è possibile tenere traccia della richiesta, in modo che il client possa sapere quando il dispositivo è stato riavviato con successo ... Ma è strano che l'API a volte risponda in questo modo. Solo se specifichi una proprietà che attiverà un lavoro di lunga durata ... Non sei sicuro di quale sia la scelta di design migliore qui.

Qualsiasi consiglio è benvenuto.

    
posta JSarwer 11.10.2017 - 23:07
fonte

2 risposte

1

RESTful Casuistry ha una discussione abbastanza buona sul riavvio; se non lo conosci già, dovresti dargli un'occhiata.

Usually for long running jobs, I would do something similar to what is described here, and return 202 accepted with the URL of the resource so you can track the operation.

Is it a good idea to do this in conjunction with partial update?

Sì.

But it feels awkward that the API will only sometimes respond like this.

Beh, non c'è niente di sbagliato che riferisca sempre che la richiesta è stata accettata.

Sono giunto alla conclusione che il modo più semplice per risolvere questo problema è pensare prima ai messaggi e poi preoccuparsi dei metadati.

In HTTP, la risposta a un'operazione non sicura è generalmente un messaggio che descrive risultato dell'azione . Se immagini un sito web che invia un modulo, potresti riavere una pagina dicendo "l'operazione è riuscita, segui questo link per vedere il risultato", o potresti riavere una pagina dicendo "l'operazione è stata accettata, segui questo link per ottenere il ultimo stato ".

La stessa idea espressa in un altro modo: stai navigando su un protocollo di integrazione; a volte l'applicazione passa allo stato finito, altre volte passa a uno stato intermedio in sospeso.

Lo stato intermedio in realtà non è intrinsecamente diverso da uno stato non riuscito, in cui viene restituita una risposta che descrive il fatto che la richiesta inoltrata è stata respinta, ed ecco cosa puoi fare al riguardo.

Il codice di stato HTTP e le intestazioni di risposta sono non la risposta; sono meta dati sollevati dalla risposta. Quello che stiamo facendo in questo caso sta prendendo semantica dalla risposta (che si rivolge al consumatore) e ne estraiamo alcuni nei metadati in modo che i componenti generici che partecipano al il protocollo ha una comprensione grossolana di ciò che sta succedendo e può agire in modo appropriato.

But it feels awkward that the API will only sometimes respond like this.

Pensa ai siti web che abbiamo oggi. A volte fai clic sul link e ti porta ai dati che ti aspetti; ma a volte invece si viene indirizzati a uno schermo di accesso perché la licenza precedente è scaduta.

In altre parole, facciamo questo tipo di cose sempre ; ci sono più stati a cui l'applicazione potrebbe passare, il server ne sceglie uno e il cliente deve capire come fare progressi.

La risposta REST a quest'ultima è hypermedia, ovvero il client deve comprendere la semantica dei collegamenti che potrebbero essere restituiti dal server e il server sceglie di rispondere con i collegamenti appropriati, incluse le annotazioni semantiche che distinguerli.

Nel tuo esempio, la maggior parte dei casi d'uso ha dati pronti immediatamente, quindi la risposta "risultato dell'operazione" includerà solo il collegamento che informa il cliente su come visualizzare il risultato

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

Per il caso di riavvio, che richiederà del tempo, il "risultato della risposta all'operazione includerà solo il collegamento che informa il cliente su come verificare l'avanzamento

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

Un altro modo di pensare a questo: la presenza del collegamento dice al cliente che una risorsa è disponibile, la relazione di collegamento dice al client quale sia la risorsa , il protocollo dice al client quali azioni può portare con quel tipo di risorsa (quali sono i metodi http supportati, quali tipi di media sono disponibili e così via).

Atom Publishing è un ottimo esempio di protocollo definito in questo modo.

    
risposta data 12.10.2017 - 13:47
fonte
2

Creare immediatamente un GUID e restituirlo nella risposta dopo l'avvio in modo asincrono del lavoro:

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

Quindi hai un metodo aggiuntivo:

DeviceUpdateStatus

Questo prende il guid come argomento e può essere interrogato in modo che il client sappia se l'aggiornamento è completato o meno.

    
risposta data 11.10.2017 - 23:15
fonte

Leggi altre domande sui tag