Progettazione API REST per pagine Web con procedure guidate


11

Ho una pagina web con il formato della procedura guidata. Il pulsante di invio all'API sarà nel 4 ° passaggio della procedura guidata. Tuttavia, desidero che i dati immessi vengano archiviati nel database prima di passare al passaggio successivo della procedura guidata. Voglio anche che l'API REST funzioni per le pagine con una sola scheda.

Quindi ho progettato l'API per eseguire una query parametro action = draft o submit. Se l'azione viene redatta, sono obbligatori solo alcuni campi. Se viene inviata un'azione, tutti i campi sono obbligatori. Le convalide nel livello di servizio dell'API REST verranno eseguite in base al parametro query. Sembra che devo specificare esplicitamente le clausole if / else nella documentazione. È una forma accettabile di design RESTful? Quale sarebbe il miglior design con questi requisiti?


3
Perché i dati provvisori devono essere archiviati nel DB?
Dan1701,

2
@ Dan1701: in modo da poter riprendere la procedura guidata da un'altra macchina. Quando si compilano moduli lunghi e complessi, potrebbero essere necessari alcuni giorni per completare tutti i dati richiesti, poiché l'utente potrebbe non avere tutti i dati necessari pronti in mano, oppure potrebbe essere necessario raccogliere altri file per il caricamento da luoghi diversi. Se puoi riprendere da un dispositivo diverso, puoi caricare la procedura guidata per caricare una foto dal telefono cellulare e continuare a digitare una lunga descrizione / argomento con una tastiera reale sul desktop, ecc.
Lie Ryan,

In tal caso, penso che la risposta di @ guillaume31 abbia un senso.
Dan1701,

Risposte:


7

Dal momento che si desidera persistere cose sul server tra i passaggi della procedura guidata, sembra perfettamente accettabile considerare ogni passaggio come una risorsa separata. Qualcosa del genere:

POST /wizard/123/step1
POST /wizard/123/step2
POST /wizard/123/step3

Includendo i collegamenti ipermediali nella risposta, è possibile informare il cliente su cosa può fare dopo questo passaggio: andare avanti o indietro per i passaggi intermedi e nulla per il passaggio finale. Puoi vederne un esempio nella Figura 5 qui .


Sto usando Angular per l'interfaccia utente. Quindi non sono sicuro di quanto sia utile la macchina a stati. Ma penso che le risorse basate sui passi sembrino più significative della gestione di un'altra tabella. Inoltre, dovrei essere in grado di inviare tutto in un unico passaggio. Oggi ci proverò con questo design. Grazie per l'aiuto.
TechCrunch,

Prego. A proposito, l'approccio "due tavoli" non si esclude a vicenda con questo. Avere una risorsa HTTP per passaggio non determina il modello a oggetti sul server delle applicazioni, per non parlare dello schema del database. È solo una rappresentazione Web.
guillaume31,

1
@TechCrunch In sostanza, Guillaume significa che l'oggetto / tabella che rappresenta il modulo può essere suddiviso in parti, in cui parte del modello viene salvata ad ogni passaggio. In effetti, puoi semplicemente fare in modo che ogni "passaggio" sia un modulo per parte dell'intero modello . E se segui questo approccio, in realtà rende l'architettura incredibilmente semplice. Ogni POST al server (creerà o) aggiornerà lo stesso modello e ogni GET caricherà lo stesso modello e ogni passaggio sarà un modulo per compilare insiemi di campi che sono semanticamente significativi (appartengono insieme). E semplicemente avere un valore booleano sul modello per in_progresso draft.
Chris Cirefice,

3

Avevo bisogno di fare qualcosa di simile qualche tempo fa, e quanto segue descrive ciò con cui finiamo.

Abbiamo due tabelle, Item e UnfinishedItem. Quando l'utente inserisce i dati con la procedura guidata, i dati vengono archiviati nella tabella UnfinishedItem. Ad ogni passaggio della procedura guidata, il server convalida i dati immessi durante tale passaggio. Quando l'utente ha terminato la procedura guidata, la procedura guidata rende un modulo nascosto / di sola lettura in una pagina di conferma che mostra tutti i dati da inviare. L'utente può rivedere questa pagina e tornare al passaggio pertinente per correggere gli errori. Una volta che l'utente è soddisfatto delle proprie voci, l'utente fa clic su Invia e la procedura guidata invia quindi tutti i dati nei campi del modulo nascosto / di sola lettura al server API. Quando il server API elabora questa richiesta, esegue nuovamente tutte le convalide eseguite durante ciascuna fase della procedura guidata ed esegue ulteriori convalide che non si adattano alle singole fasi (ad esempio convalide globali, convalide costose).

I vantaggi dell'approccio a due tabelle:

  • nel database, è possibile avere vincoli più severi nella tabella Item rispetto alla tabella UnfinishedItem; non è necessario disporre di colonne opzionali che saranno effettivamente richieste al termine della procedura guidata.

  • Le query aggregate sugli articoli finiti per la segnalazione sono più semplici in quanto non è necessario ricordare di escludere gli oggetti non finiti. Nel nostro caso, non abbiamo mai avuto bisogno di fare query aggregate tra Item e UnfinishedItems, quindi questo non è un problema.

Lo svantaggio:

  • È incline alla duplicazione della logica di validazione. Il web framework che abbiamo usato, Django, lo rende un po 'più sopportabile poiché abbiamo usato l'ereditarietà dei modelli con un po' di metamagia per modificare i vincoli di cui abbiamo bisogno per essere diversi in Item e UnfinishedItem. Django genera la maggior parte del database e la convalida dei moduli dal modello e su di esso è necessario solo hackerare alcune convalide aggiuntive.

Altre possibilità che ho considerato e perché non siamo andati con loro:

  • salvataggio dei dati nei cookie o nella memoria locale: l'utente non può continuare a inviare da un dispositivo diverso o se elimina la cronologia del browser
  • archiviare UnfinishedItem come dati non strutturati (ad es. JSON) nel database o nel datastore secondario: dovrò definire la logica di analisi e non posso usare la validazione automatica del modello / modulo di Django.
  • esegui la convalida per passaggio sul lato client: dovrò duplicare la logica di convalida tra Python / Django e JavaScript.

1
+1 per indicare convalide su modelli di tipo "bozza" e modelli "finiti"; Non ci ho pensato, ed è un punto importante che dovrebbe essere preso in considerazione. Altrimenti probabilmente avresti un sacco di ifdichiarazioni che controllano lo stato della bozza in tutte le tue convalide, il che non sarebbe giusto. Anche se alcuni framework molto sofisticati come Ruby on Rails potrebbero semplificare significativamente questo problema se implementati correttamente.
Chris Cirefice,

1

L'ho implementato in un modo simile a un mix di soluzioni @ guillauma31 e @Lie Ryan.

Ecco i concetti chiave:

  1. Esiste una procedura guidata in 3 passaggi che può essere parzialmente persistente fino al completamento.
  2. Ogni passaggio ha una propria risorsa (es . /users/:id_user/profile/step_1: ,, .../step_2ecc.)
  3. Ad ogni passaggio, i dati e lo stato di completamento possono essere recuperati attraverso richieste GET e persistenti attraverso richieste PATCH.
  4. Ogni risorsa ha le proprie regole di convalida per i dati inseriti.
  5. Ogni passaggio restituisce una chiave che deve essere utilizzata nell'input del passaggio successivo per garantire la sequenza. Una volta utilizzato o generato uno nuovo, questo token scade.
  6. Nel passaggio finale, abbiamo tutti i dati necessari sul database e viene visualizzata una schermata di conferma. Questa conferma chiama un'altra risorsa per contrassegnare i dati come completi (ad es .:) .../profile/confirm. Questa risorsa non deve ricevere di nuovo tutti i dati. Contrassegna solo i dati come corretti e completi.
  7. Esiste una routine pianificata che cancella queste voci incomplete che hanno più di qualche giorno.

I ragazzi front-end devono prendersi cura dei token per far funzionare il flusso avanti e indietro del mago.

L'API è apolide e atomica.

Per far funzionare una "procedura guidata in un solo passaggio" con questa configurazione, è necessario modificare alcune cose, come rimuovere il flusso di token o creare una risorsa per restituire i token in base al tipo di procedura guidata o persino creare una nuova risorsa solo per riempire questo singolo specifico procedura guidata (come PUT /users/:id_user/profile/).

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.