Para incluir un ID de recurso en la carga útil o derivar de URI

13

Al diseñar una API, nos encontramos con la pregunta de si una carga útil PUT debe contener la ID del recurso que se está actualizando.

Esto es lo que tenemos actualmente:

PUT /users/123 Payload: {name: "Adrian"}

Nuestro código de ruta extrae la ID del URI y continúa con la actualización.

Los primeros usuarios de nuestra API se preguntan por qué no permitimos ID en la carga útil:

PUT /users/123 Payload: {id: 123, name: "Adrian"}

La razón por la que no lo permitimos es porque la ID está duplicada, en la carga útil y el URI.

Pensando en esto un poco más, estamos acoplando el recurso al URI.

Si el URI no tiene la ID, se deberá modificar la carga útil:

PUT /no/id/here Payload: {name: "Adrian"} < What user???

¿Hay alguna razón para no hacerlo?

Adrian Lynch
fuente

Respuestas:

14

Se supone que debe acoplar el Identificador uniforme de recursos al recurso .

Cuando REST se implementa con HTTP, usa GET para recuperar el valor actual del recurso y PUT para establecer un nuevo valor. El GET no tiene una carga útil, por lo que el recurso debe ser identificado por el URI. Y el PUT se realiza lógicamente en el mismo URI y la carga útil debe verse exactamente como lo que desea que devuelva el próximo GET.

Puede usar POST para diferentes URI, pero solo tendría menos sentido ya que sería innecesariamente asimétrico al GET. POST a URI común solo podría tener sentido para crear nuevos recursos ( POST /users/new, carga útil: {name: "Adrian"}respuesta {id: 345, name: "Adrian"}), pero eso no es idempotente y, por lo tanto, debe evitarse si te esfuerzas por REST¹. En su lugar, debe reservar ID con una llamada y luego usar PUT para establecer la nueva ID; eso es tolerante a fallas, porque si la primera solicitud falla, la reserva de ID puede expirar eventualmente y PUTes idempotente. O use UUID generado por el cliente.


Definition La definición de REST no dice nada sobre idempotencia, por lo que realmente no puedo afirmar que no es REST si tiene operaciones no idempotentes. Eso no cambia el hecho de que apegarse a las solicitudes idempotentes hace que las cosas sean más confiables sin complicarlas y, por lo tanto, se recomienda.

Jan Hudec
fuente
3
Hasta donde yo sé, una solicitud POST no tiene que ser idempotente. Por lo tanto, no veo ningún problema con la publicación en /users(no es necesario agregar 'nuevo').
lex82
gracias por su respuesta. Veo que es una buena característica tener idempotencia para todas las solicitudes, pero ¿quién dice que esto es necesario para una API REST? Ciertamente, muchas API que se llaman RESTful permiten solicitudes POST no idempotentes (especialmente cuando el servidor genera identificadores para nuevos recursos). Pero incluso si aplica una definición muy estricta de REST, no veo qué restricción arquitectónica se viola con POST no idempotentes como en el ejemplo anterior.
lex82
Ciertamente puedo crear imágenes de solicitudes POST que violan una restricción. Simplemente no veo por qué publicar un recurso en una colección y dejar que el servidor decida sobre su identificación es una violación de las restricciones REST.
lex82
Continuemos esta discusión en el chat .
lex82
3
La idea de POST no es ser idempotente ...
EralpB
2

Pensando en esto un poco más, estamos acoplando el recurso al URI.

Si el URI no tiene la ID, se deberá modificar la carga útil:

PUT / no / id / here Carga útil: {nombre: "Adrian"} <¿Qué usuario?

¿Hay alguna razón para no hacerlo?

La respuesta a esta pregunta depende de si desea permitir que el cliente cambie la identificación.

Si el cliente puede cambiar la ID, a través de un PUT, entonces el URI para el recurso cambiará, y debe proporcionar un 301 Moved Permanently cada vez que un recurso acceda al viejo URI.

Entonces, por ejemplo, comienzas con un recurso en

/users/123

y el cliente pone lo siguiente en el recurso

{id: 222, name: "Adrian"}

el recurso ha sido actualizado y su URI es ahora

/users/222

El Locationcampo en la respuesta PUT debe contener el nuevo URI, y si va a /users/123, debe obtener una 301respuesta con el campo Ubicación apuntando al nuevo /users/222recurso.

En la mayoría de los casos, aunque en realidad no desea que el cliente pueda cambiar la ID, ya que esto puede volverse bastante desordenado con bastante rapidez. En ese caso, la ID es algo que solo el servidor puede cambiar, y debe dejarla fuera del cuerpo PUT, ya que el cliente no puede actualizar este estado.

Si PONE un requerimiento a un URI diferente en el mismo recurso, diga

/users/adian_lync

entonces, si ese recurso no existe, el servidor debería crearlo y crear una ID cuando lo esté haciendo

Cormac Mulhall
fuente
1
La razón por la que cuestionamos la ubicación de la ID en la carga útil se debió a que Backbone.js pasó la ID en una solicitud PUT de forma predeterminada. Podemos evitar que suceda, pero ahora quiero saber por qué ese es el comportamiento predeterminado.
Adrian Lynch
1
Me temo que no estoy familiarizado con Backbone.js. Parece innecesario si la ID también se incluye en la URL. Quizás un descuido por parte de los desarrolladores
Cormac Mulhall