¿Cuál es el mejor patrón para agregar un elemento existente a una colección en REST API?

23

Estoy diseñando una API REST pragmática y estoy un poco atascado en la mejor forma de agregar entidades existentes a una colección. Mi modelo de dominio incluye un proyecto que tiene una colección de sitios. Esta es una relación estricta de muchos a muchos y no tengo necesidad de crear una entidad que modele explícitamente la relación (es decir, ProjectSite).

Mi API permitirá a los consumidores agregar un sitio existente a un proyecto. Donde estoy colgado es que los únicos datos que realmente necesito son ProjectId y SiteId. Mi idea inicial fue:

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

Pero también pensé en

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

con una entidad del sitio enviada como contenido JSON.

La opción 1 es simple y funciona, pero no se siente del todo bien, y tengo otras relaciones que no pueden seguir este patrón, por lo que agrega inconsistencia a mi API.

La opción 2 se siente mejor pero lleva a dos preocupaciones:

  • ¿Debo crear un sitio o lanzar una excepción si se publica un nuevo sitio (SiteId = 0)?
  • Debido a que solo necesito ProjectId y SiteId para crear la relación, el Sitio podría publicarse con datos incorrectos o faltantes para otras propiedades.

Una tercera opción es proporcionar un punto final simple únicamente para crear y eliminar la relación. Este punto final esperaría una carga útil JSON que contenga solo ProjectId y SiteId.

¿Qué piensas?

Jamie Ide
fuente
2
Ver también: stackoverflow.com/questions/2001773/…
Rory Hunter
@RoryHunter Hay una discusión interesante en ese enlace, pero nada que elimine mi incertidumbre. Me gusta especialmente que la respuesta aceptada dice "Entendiste bien". y el segundo lugar (aunque por un amplio margen) responde "En pocas palabras, estás haciendo esto completamente al revés".
Jamie Ide
Su primera opción está bien, aunque usaría PUT en lugar de POST ya que el cliente tiene el control de la identidad que se agrega a la colección. Su primera preocupación con la opción 2 es totalmente suya, si no desea nuevos sitios, no arroje una excepción, sino que devuelva uno de los códigos 4xx. Su segunda preocupación no es ni aquí ni allá. No debe publicar un sitio completo de todos modos a menos que permita adiciones. Agregar un sitio existente debe tener la identificación solo cuando esté modificando el sitio, pero solo la colección "ProjectSite" (incluso si no crea un recurso separado para él).
Marjan Venema

Respuestas:

14

POST es el verbo "agregar", y también el verbo "procesar". PUT es el verbo "crear / actualizar" (para identificadores conocidos), y casi parece la opción correcta aquí, porque se conoce el URI de destino completo. projectIdy siteIdya existe, por lo que no necesita "PUBLICAR en una colección" para generar una nueva ID.

El problema con PUT es que requiere que el cuerpo sea la representación del recurso que está PUTING. Pero la intención aquí es agregar al recurso de colección "proyecto / sitios", en lugar de actualizar el recurso del sitio.

¿Qué sucede si alguien pone una representación JSON completa de un sitio existente? ¿Debería actualizar la colección y actualizar el objeto? Podrías apoyar eso, pero parece que esa no es la intención. Como dijiste,

los únicos datos que realmente necesito son ProjectId y SiteId

Por el contrario, trataría de PUBLICAR siteIden la colección y confiar en la naturaleza de "agregar" y "procesar" de POST:

PUBLICAR myapi / projects / {projectId} / sites

{'carné de identidad': '...' }

Como está modificando el recurso de colección de sitios y no el recurso del sitio , ese es el URI que desea. POST puede saber "agregar / procesar" y agregar el elemento con esa identificación a la colección de sitios del proyecto.

Eso todavía deja la puerta abierta para crear nuevos sitios para el proyecto al desarrollar el JSON y omitir la identificación. "No id" == "crear desde cero". Pero si el URI de la colección obtiene una identificación y nada más, está bastante claro lo que debe suceder.

Interesante pregunta. :)

Robar
fuente
Soy de la fe que cree que POST es para crear y PUT es para actualizar, pero su conclusión es donde terminé ayer. Lo bueno es que gracias al enrutamiento de atributos en la API web, tengo el código en un controlador ProjectSites, por lo que el código está bien organizado.
Jamie Ide
Creo que la razón definitoria que debe usar en POSTlugar de PUTo PATCHaquí es que no tiene toda la Siteentidad para poner en el sitesrecurso. Solo tiene la identificación, que requiere procesamiento para agregarla a la colección.
aplastar
4

Usamos el Patchmétodo para cosas como esta. Lo que desea hacer es modificar un proyecto existente para agregarle un sitio.

Entonces algo como esto funcionaría

PATCH myapi/projects/{id} 

con la entidad del sitio (s) como JSON / JSONArray en el cuerpo de la solicitud.

De esa manera, puede usar la misma URL para modificar diferentes partes del proyecto si lo necesita: su código en la implementación debe ser lo suficientemente inteligente como para manejar esta modificación parcial del recurso.

juan
fuente
Enfoque interesante Tengo un modelo de dominio antiguo "rico" (es decir, altamente dependiente) y Project especialmente tiene muchas colecciones colgando de él. Detectar el tipo de entidad que está en la solicitud sería un desafío y no encaja con mi objetivo pragmático.
Jamie Ide
¿Por qué un desafío? Si tiene esas restricciones, siempre puede usar un JSON que explique lo que está enviando ... como {"sites": [], "other-stuff": {}}, entonces puede ramificar su código para manejar todos esos "subjsons" muy fácilmente. Realmente depende de su problema específico, pero aún así recomendaría usar PATCH ya que está diseñado específicamente para este tipo de cosas.
juan
Los inconvenientes que veo son 1) API no comunica explícitamente qué colecciones permiten cambios; 2) no puede aprovechar el enlace de parámetros de la API web; 3) gran cambio o si la declaración.
Jamie Ide
Nunca he visto el método de parche utilizado en ningún otro lugar
NimChimpsky
¿No PATCHesperaría que la entidad completa se pase como su valor aquí, en lugar de una identificación que apunta a alguna entidad?
aplastar