Endpoint REST per mostrare un'anteprima prima del POST


17

Sto progettando una nuova applicazione Web che è alimentata da un backend REST e frontend HTML + JS.

C'è un metodo POST su di esso per cambiare un'entità (chiamiamo Config), che ha diversi effetti collaterali nello stato di molti elementi dell'applicazione. Supponiamo che il POST venga eseguito in questo modo:

POST /api/config BODY {config: ....}

Per questo motivo, vorrei mostrare un'anteprima prima di apportare tali modifiche, affinché l'utente finale possa notare cosa cambierà.

La prima cosa a cui ho pensato è creare un endpoint GET per l'anteprima, inviando il corpo del nuovo stato dell'entità. Per di qua:

GET /api/preview/items BODY {config: ....}

Potrebbe mostrare il nuovo stato per gli articoli con la nuova configurazione.

GET /api/preview/sales BODY {config: ....}

Potrebbe mostrare il nuovo stato per le vendite con la nuova configurazione.

Sembra una buona idea usare il verbo GET poiché non sto modificando lo stato dell'applicazione. Tuttavia, l'uso di un corpo di richiesta con richieste GET sembra scoraggiato .

C'è qualche buona pratica a riguardo? Un'altra scelta potrebbe essere quella di archiviare la configurazione come bozza con un metodo e visualizzare i risultati con altri, ma richiederebbe un passaggio aggiuntivo e la gestione delle bozze nel server:

POST /api/preview/config BODY {config: ....}

GET /api/preview/items?idPreviewConfig=1

Cosa potrebbe essere esattamente questa configurazione e in che modo influenza itemso sales? Influisce sulla rappresentazione dell'entità restituita?
Andy,

Supponiamo che gli articoli e le vendite siano influenzati dalle modifiche apportate nella configurazione.
Xtreme Biker ripristina Monica il

Ma cosa significano i cambiamenti? Cambia l'insieme delle entità restituite? Cambia la struttura restituita?
Andy,

In realtà cambia i valori per itemse sales(non la struttura), a seconda della configurazione che POST.
Xtreme Biker ripristina Monica il

E quanto è grande la configurazione esattamente? Può crescere fino a diverse centinaia di kilobyte o anche di più?
Andy,

Risposte:


27

È troppo specifico per il dominio per avere un supporto nativo in HTTP.

Invece, è possibile effettuare una delle seguenti operazioni:

  1. Avere un POST /api/config/preview. Sul lato server, l'applicazione saprà che non dovrebbe modificare la configurazione effettiva, ma combinare quella effettiva con quella che hai pubblicato e restituire il risultato indicando cosa è stato modificato.

    Successivamente, se l'utente è soddisfatto del risultato, eseguirà un POST /api/configcontenente lo stesso payload della richiesta precedente. Ciò sovrascriverà efficacemente la configurazione.

    Il vantaggio di questo approccio è che non stai apportando modifiche all'API corrente. I client che non necessitano della funzione di anteprima sarebbero comunque in grado di aggiornare le voci come prima.

    Lo svantaggio è che quando il corpo è grande, significherebbe che sarebbe necessario inviarlo due volte al server. In tal caso, è possibile utilizzare l'approccio successivo.

  2. Avere un oggetto POST /api/config/prepareche ricorda ciò che è stato inviato in un record temporaneo e restituisce due elementi: l'ID del record temporaneo (ad esempio 12345) e l'anteprima delle modifiche.

    Se l'utente è soddisfatto del risultato, eseguirà un POST /api/config/commit/12345per archiviare definitivamente le modifiche. In caso contrario, il record temporaneo potrebbe essere conservato per qualche tempo e quindi scartato da un processo cron.

    Il vantaggio è che, anche in questo caso, è possibile mantenere POST /api/configintatto l'originale e i client che non necessitano di un'anteprima non si romperanno.

    Gli svantaggi sono che (1) gestire la rimozione di record temporanei può essere complicato (cosa ti fa pensare che un'ora sia sufficiente? Che cosa succede se dieci minuti dopo, esaurisci la memoria? In che modo i client gestiscono un HTTP 404 quando eseguono un commit di un record che è scaduto?) e che (2) la presentazione in due passaggi di un record può essere più complicata del necessario.

  3. Sposta la logica di anteprima sul lato client.


Che ne dici di inviare un'intestazione che dice "non persistere, mostrami solo il what-if"? Lo modificherò nella risposta se per te va bene @ArseniMourzenko
marstato

1
@marstato: personalmente, non mi piacciono particolarmente le intestazioni HTTP per quell'uso. Anche se può avere senso per le altre persone, quindi sto bene se modifichi la mia risposta. Nota che puoi anche pubblicare la tua risposta, che consentirebbe ad altri di votarla (e otterrai punti reputazione).
Arseni Mourzenko,

Immagino che l'opzione 1 si adatti meglio al mio caso. Quindi POST la configurazione dell'anteprima e hai le modifiche nel risultato, invece di dover definire endpoint di anteprima per ciascuna delle entità definite. Sembra ragionevole. L'unica cosa è che stai usando un POST per non apportare modifiche al server, tecnicamente parlando. L'opzione 3 non è praticabile nel mio caso.
Xtreme Biker ripristina Monica il

1
@PedroWerneck Puoi espanderci? Mi sembra che l'opzione 2 definisca un'altra entità (una bozza di configurazione) e fornisca modi senza stato per interagire con loro.
Andrew dice di reintegrare Monica il

1
@PedroWerneck È stateful allo stesso modo in cui memorizzare una configurazione sul server è stateful. Quindi l'applicazione è già stateful dalla tua prospettiva e così sono tutte le opzioni per aggiungere questa funzionalità.
jpmc26,

10

Lo scopo di utilizzare specifici verbi HTTP per diverse chiamate API in REST è sfruttare le meccaniche e le aspettative HTTP esistenti.

L'uso di un GET in questo caso sembra andare contro entrambi.

A. Il cliente deve includere un corpo con un OTTENERE? inaspettato

B. Il server restituisce una risposta diversa per ottenere a seconda del corpo? rompe le specifiche e la meccanica della cache

Se stai lottando con domande RESTful, la mia regola è chiedermi.

"In che modo è meglio che usare semplicemente POST per tutto?"

A meno che non ci sia un vantaggio immediato e ovvio, segui la strategia Just Use POST Stupid (JUPS)


Hahaha buona cattura
Xtreme Biker ripristina Monica il

@Ewan ... indipendentemente dal fatto che si tratti o meno di un approccio pragmatico ... se si utilizza POST per tutto, è necessario notare che in realtà non è RESTful.
Allenph,

1
bene, a meno che POST sia la scelta appropriata per tutti i tuoi metodi. E non è che ci sia una regola oggettiva che puoi applicare, discuteremo semplicemente delle nostre interpretazioni soggettive di ciò che è poco più di una linea guida.
Ewan,

6

Puoi inviare un'intestazione che indica al server "non persistere, mostrami solo il risultato se lo facessi". Per esempio

POST /api/config HTTP/1.1
Host: api.mysite.com
Content-Type: application/json
Persistence-Options: simulate

{
   "config": {
      "key": "value"
   }
}

A cui il server potrebbe rispondere:

HTTP/1.1 200 OK
Persistence-Options: simulated
Content-Type: application/json

-- preview --

Si noti che, se si utilizzano transazioni O / RM basate su unità di lavoro e / o per richiesta con il proprio DB, è possibile implementare facilmente questa funzionalità per tutti gli endpoint senza richiedere lavoro su alcun endpoint particolare: se una richiesta arriva con quell'opzione , ripristina la transazione / unità di lavoro anziché eseguirne il commit.



@PeterRader buon punto, rimosso ilX-
marstato

Prego. Diresti che un'entità sotto simulazione dovrebbe essere rappresentata come "sotto simulazione"?
Peter Rader,

No; questo è il punto di una simulazione, vero? Il valore dell'intestazione potrebbe anche essere, nonema ciò - per i miei gusti - sarebbe in contraddizione con la natura del POSTmetodo.
Marstato,

2

Suggerirei di trattarlo nello stesso modo in cui trattate le ricerche. Vorrei impostare un endpoint POST in /api/config/previewcui CREA una nuova anteprima. Quindi impostarei un endpoint PUT o PATCH a api/configseconda che tu intenda modificare la configurazione corrente o semplicemente sostituire l'intera configurazione (presumibilmente nel primo caso invierai l'anteprima appena creata).


0

Insieme alle altre buone risposte, un'altra opzione potrebbe essere quella di pubblicare la configurazione come menzionato e avere anche un processo di rollback disponibile. Penso che, come la metodologia Agile, è meglio avere meno paura dei cambiamenti avendo procedure più granulari, ripetibili e testate, e questo ti darebbe un backup quando ne avrai bisogno, riducendo il rischio a poco o nessuno, a seconda dell'applicazione .

Inoltre, se si verificano errori di configurazione che interessano l'intero sistema, si desidera gestirlo in modo più attivo e, in tal caso, perché non limitarsi a visualizzare in anteprima le modifiche in quel punto, dal punto di vista del server o del client. Tuttavia, posso vedere come questa funzionalità di anteprima possa essere più costosa da sviluppare, ai casi d'uso hanno il proprio set di passaggi diversi da seguire e testare.


0

RFC6648 deprezza i nuovi X-costrutti, quindi devo votare contro l'idea di inviare un nuovo campo di intestazione. REST è uno stile di architettura, quello di cui parliamo è RESTful - ma per il momento lo ignoriamo.

Poiché REST è rappresentativo (e una simulazione non ha rappresentazione nella realtà) e Stateful (e una simulazione non è uno stato fino al suo impegno), dobbiamo avere un nuovo ambito, come un ambito di simulazione. Ma dobbiamo chiamarla emulazione anziché simulazione perché la simulazione include il processo di simulazione, ma significa che abbiamo uno stato permanente, una soluzione ideale di una simulazione: un'emulazione. Quindi dobbiamo chiamarlo emulazione nell'URL. Questa potrebbe anche essere una buona soluzione:

GET  /api/emulation - 200 OK {first:1, last:123}
POST /api/emulation/124 - 200 OK
GET  /api/emulation/124/config - 200 OK {config:{tax:8}}
PUT  /api/emulation/124/config {config:{tax:16}} - 200 OK {config:{tax:16}}
GET  /api/emulation/124/items - 200 OK [first:1, last: 3000]
GET  /api/emulation/124/items/1 - 200 OK {price:1.79, name:'Cup'}
--- show emulation ---
--- commit emulation ---
PUT /api/config {config:{tax:16}}
DELETE /api/emulation/124 - 200 OK

C'è un altro approccio .... potresti notare che avere molte richieste dal client HTML / JavaScript può produrre troppe richieste , ciò che raggiunge il limite di circa 17 richieste contemporaneamente (dai un'occhiata a questa pagina ). È possibile scambiare l'utilizzo di REST e invece di fornire stati di oggetti scadenti è possibile fornire stati di pagina ricchi specifici dell'utente. Esempio:

GET /user/123/config - 200 OK {user:'Tim', date:3298347239847, currentItem:123, 
                  roles:['Admin','Customer'], config:{tax:16}, unsavedChanges:true, ...}

Cordiali saluti

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.