Qual è il modello migliore per aggiungere un elemento esistente a una raccolta nell'API REST?


23

Sto progettando un'API REST pragmatica e sono un po 'bloccato sul modo migliore per aggiungere entità esistenti a una raccolta. Il mio modello di dominio include un progetto che ha una raccolta di siti. Questa è una stretta relazione molti-a-molti e non ho bisogno di creare un'entità che modella esplicitamente la relazione (cioè ProjectSite).

La mia API consentirà ai consumatori di aggiungere un sito esistente a un progetto. Il punto in cui vengo bloccato è che gli unici dati di cui ho veramente bisogno sono ProjectId e SiteId. La mia idea iniziale era:

1. POST myapi/projects/{projectId}/sites/{siteId}

Ma ci ho pensato anche io

2. POST myapi/projects/{projectId}/sites

con un'entità sito inviata come contenuto JSON.

L'opzione 1 è semplice e funziona ma non sembra del tutto corretta, e ho altre relazioni che non possono seguire questo schema, quindi aggiunge incoerenza alla mia API.

L'opzione 2 si sente meglio, ma porta a due preoccupazioni:

  • Devo creare un sito o generare un'eccezione se viene pubblicato un nuovo sito (SiteId = 0)?
  • Poiché ho solo bisogno di ProjectId e SiteId per creare la relazione, il sito potrebbe essere pubblicato con dati errati o mancanti per altre proprietà.

Una terza opzione è fornire un endpoint semplice esclusivamente per la creazione e l'eliminazione della relazione. Questo endpoint prevede un payload JSON contenente solo ProjectId e SiteId.

Cosa pensi?



@RoryHunter C'è qualche discussione interessante in quel link ma niente che elimini la mia incertezza. Mi piace soprattutto che la risposta accettata reciti "Hai capito bene". e il 2 ° posto (anche se con un ampio margine) risponde "In poche parole, lo stai facendo completamente indietro."
Jamie Ide,

La tua prima opzione va bene anche se userei PUT invece di POST poiché il client ha il controllo dell'identità che viene aggiunta alla raccolta. La tua prima preoccupazione con l'opzione 2 dipende interamente da te, se non desideri nuovi siti, non generare un'eccezione ma restituire uno dei codici 4xx. La tua seconda preoccupazione non è né qui né lì. Non dovresti pubblicare un intero sito comunque a meno che tu non autorizzi aggiunte. L'aggiunta di un sito esistente dovrebbe avere l'id solo quando si modifica il sito ma solo la raccolta "ProjectSite" (anche se non si crea una risorsa separata per esso).
Marjan Venema,

Risposte:


14

POST è il verbo "append" e anche il verbo "processing". PUT è il verbo "crea / aggiorna" (per identificatori noti), e qui sembra quasi la scelta giusta, perché è noto l'URI di destinazione completo. projectIded siteIdesiste già, quindi non è necessario "POSTARE una raccolta" per produrre un nuovo ID.

Il problema con PUT è che richiede che il corpo sia la rappresentazione della risorsa che stai mettendo. Ma l'intento qui è quello di aggiungere alla risorsa di raccolta "progetto / siti", piuttosto che aggiornare la risorsa del sito.

Cosa succede se qualcuno METTE una rappresentazione JSON completa di un sito esistente? Dovresti aggiornare la raccolta e aggiornare l'oggetto? Potresti supportarlo, ma sembra che non sia questo l'intento. Come hai detto,

gli unici dati di cui ho veramente bisogno sono ProjectId e SiteId

Piuttosto, proverei a POSTARE la siteIdraccolta e mi affido alla natura "append" e "process" di POST:

POST myapi / progetti / {projectId} / siti

{'id': '...'}

Dato che stai modificando la risorsa di raccolta siti e non la risorsa sito , questo è l'URI che desideri. Il POST può sapere di "aggiungere / elaborare" e aggiungere l'elemento con quell'id alla raccolta siti del progetto.

Ciò lascia ancora aperta la porta alla creazione di siti nuovi di zecca per il progetto completando il JSON e omettendo l'id. "No id" == "crea da zero". Ma se l'URI della raccolta ottiene un ID e nient'altro, è abbastanza chiaro cosa deve accadere.

Domanda interessante. :)


Sono convinto che POST sia per creare e PUT sia per l'aggiornamento, ma la tua conclusione è dove sono finito ieri. La cosa bella è che grazie al routing degli attributi nell'API Web, ho il codice in un controller ProjectSites, quindi il codice è ben organizzato.
Jamie Ide,

Penso che la ragione principale che devi usare al POSTposto di PUTo PATCHqui sia che non hai l'intera Siteentità da inserire nella sitesrisorsa. Hai solo l'id, che richiede l'elaborazione per poterlo aggiungere alla raccolta.
schiaccia il

4

Usiamo il Patchmetodo per cose come questa. Quello che vuoi fare è modificare un Progetto esistente per aggiungere un Sito ad esso.

Quindi qualcosa del genere funzionerebbe

PATCH myapi/projects/{id} 

con l'entità Sito / i come JSON / JSONArray nel corpo della richiesta.

In questo modo è possibile utilizzare lo stesso URL per modificare parti diverse del Progetto, se necessario - il codice nell'implementazione deve essere abbastanza intelligente da gestire questa modifica parziale della risorsa.


Approccio interessante Ho un modello di dominio "ricco" (cioè altamente dipendente) e Project ha in particolare molte collezioni sospese. Rilevare il tipo di entità presente nella richiesta sarebbe una sfida e non rientra nel mio obiettivo pragmatico.
Jamie Ide,

Perché una sfida? Se hai queste restrizioni, puoi sempre usare un JSON che rende esplicito ciò che sta inviando ... ad esempio {"sites": [], "other-stuff": {}}, puoi quindi ramificare il tuo codice per gestire tutti quei "subjson" molto facilmente. Dipende molto dal tuo problema specifico, ma consiglierei comunque di usare PATCH in quanto è progettato specificamente per questo tipo di cose.
juan,

I lati negativi che vedo sono 1) L'API non comunica esplicitamente quali raccolte consentono le modifiche; 2) impossibile sfruttare l'associazione dei parametri dell'API Web; 3) big switch o if statement.
Jamie Ide,

Non ho mai visto il metodo patch usato da nessun'altra parte
NimChimpsky

Non ti PATCHaspetteresti anche che l'intera entità venga passata come valore qui, piuttosto che un ID che punta a qualche entità?
schiaccia il
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.