Come progettare un'API REST per gestire operazioni non CRUD?


11

Sto cercando di convertire un set di servizi basati su SOAP in un'API RESTful.

Ho iniziato identificando le risorse analizzando i nomi delle operazioni e ho ottenuto la risorsa Subscription.

Quando devo aggiornare lo stato della sottoscrizione, non posso semplicemente inviare una POSTrichiesta al server, perché non ho accesso diretto alle risorse, ma devo chiamare alcune operazioni in stile RPC per aggiornare le loro proprietà. Inoltre, solo e solo se sto cambiando lo stato dell'abbonamento su "attivo", è necessaria una chiamata aggiuntiva a un servizio esterno.

In questi casi, qual è la migliore pratica per gestire le operazioni sottostanti?

La soluzione che mi è venuta in mente è quella di utilizzare i parametri di query, in modo che se ho bisogno di chiamare il servizio di attivazione, posso usare qualcosa come:

POST /subscriptions/{subscriptionid}/?activate=true

Considerando che non riesco ad aggiornare direttamente i miei campi oggetto Abbonamento, esiste qualche buona pratica per gestire questo tipo di conversione?

Aggiornamento 1:

Posso inserire nel corpo della mia richiesta POST alcuni valori, ad esempio "stato": "attivo"

e controllare all'interno del mio servizio le operazioni appropriate da attivare.


La mappatura di REST dei comandi sui verbi HTTP fallisce con operazioni complesse. Stai meglio facendo una chiamata in stile RPC POST activSubscription / {id} nessuno ne sarà confuso
Ewan,

@Ewan Non sono sicuro che questo sia conforme al modello RESTful, ma ho trovato un'altra soluzione: nel mio codice posso chiamare il corretto funzionamento in stile RPC in base al payload di input (posso passare state = attivo nel corpo di la mia richiesta di posta, il codice chiamerà il codice di attivazione)
Vektor88,

1
Un aggiornamento a una risorsa esistente come questa dovrebbe essere un PATCH e il corpo della query è quindi un modello parziale di ciò che stai cambiando. Un POST dovrebbe essere una richiesta che crea una risorsa. Questa distinzione, oltre ad essere più chiara per l'utente, renderà più facile al tuo codice sapere quando si sta verificando questa operazione piuttosto che un post di risorse.
Sig. Cochese,

1
@ Vektor88 In genere, ma quelle sono operazioni idempotenti in cui è necessario passare l'intera rappresentazione dello stato delle risorse. Questo caso d'uso sembra molto più simile a un aggiornamento parziale, che si adatta molto bene a un PATCH.
Sig. Cochese,

1
@MrCochese POST non è idempotente.
JimmyJames,

Risposte:


8

Devi guardare questo discorso di Jim Webber.

Quando devo aggiornare lo stato della sottoscrizione, non posso semplicemente inviare una richiesta POST al server, perché non ho accesso diretto alle risorse, ma devo chiamare alcune operazioni in stile RPC per aggiornare le loro proprietà. Inoltre, solo e solo se sto cambiando lo stato dell'abbonamento su "attivo", è necessaria una chiamata aggiuntiva a un servizio esterno.

Pensa "alla messaggistica"; invia un messaggio al tuo dominio, descrivendo cosa vuoi che accada. L'effetto collaterale del messaggio è che il tuo modello di dominio cambia effettivamente il suo stato. La "risorsa" è la coda dei messaggi.

POST /subscriptions/{subscriptionid}/?activate=true

L'ortografia del nome della risorsa non ha importanza per le macchine; ma le persone tendono a diventare pignole quando gli identificatori che usi rompono dalla convenzione che le risorse sono "sostantivi".

Inoltre, stiamo parlando di una risorsa a cui è subordinata /subscriptions/{subscriptionid}, quindi la convenzione (vedi RFC 3986 ) richiede di esprimere quella relazione con un segmento di percorso, piuttosto che usare la parte della query.

Quindi queste ortografie potrebbero essere ragionevoli

POST /subscriptions/{subscriptionid}/messages
POST /subscriptions/{subscriptionid}/activations

1
Il discorso di Jim Webber è disponibile su youtube.com/watch?v=aQVSzMV8DWc
user674669

0

Se è un flag booleano per attivare / disattivare roba, direi che l'impostazione predefinita è utilizzare JSON:

POST /subscriptions/{subscriptionid}/
{
    format: 0,
    subscription: 
    {
        active: false
    }
}

Questo può essere facilmente esteso se si desidera supportare più proprietà. Un altro approccio sta dando il suo endpoint:

POST /subscriptions/{subscriptionid}/active/
DELETE /subscriptions/{subscriptionid}/active/

Personalmente, lo userei solo se lo activestato di questo evento ha bisogno / ha proprietà che puoi passare / ottenere in JSON, come un ID utente o un'impostazione.

Se non è un valore booleano ma solo un'azione che devi attivare ma non hai bisogno / non hai alcun feedback sullo stato (tranne un immediato 200 OK), utilizzerei un endpoint come questo per attivarlo come un RPC:

POST /subscriptions/{subscriptionid}/activate/

In caso di dubbi, leggi questo: http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api#restful (vedi "Che dire delle azioni che non rientrano nel mondo delle operazioni CRUD? ")


0

REST non è funzionale. Activateè un verbo e non può essere uno stato, Activeè uno stato.

Poiché RESTful non funziona, non è possibile indicare a un servizio RESTful cosa fare, ma è possibile aggiungere lavoro per la coda di un servizio.

Guarda questo:

PUT /subscriptionQueue
subscriptionId={subscriptionId}
active=true

Questa richiesta è RESTful e supporta tutti i vantaggi di RESTful (come prestazioni, acido ...)

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.