restful - rest vs soap
¿Cuál es la forma tranquila de representar una operación de clonación de recursos en la URL? (3)
Como no hay copia ni método de clonación en HTTP, depende de ti lo que quieras hacer. En este caso, un POST
parece perfectamente razonable, pero otros estándares han tomado diferentes enfoques:
- WebDAV agregó un método
COPY
. - Amazon S3 utiliza
PUT
sin cuerpo y un encabezado especialx-amz-copy-source
. Llaman a estoPUT Object - Copy
.
Ambos enfoques suponen que conoce el URI de destino. Su ejemplo parece carecer de un uri de destino conocido, por lo que prácticamente debe usar POST. No puede usar PUT o COPY porque su operación de creación no es idempotente.
Si su servicio define POST /resources
como "crear un nuevo recurso", ¿por qué no simplemente definir otra forma de especificar el recurso que no sea el cuerpo del POST? Ej. POST /resources?source=/resources/10
con un cuerpo vacío.
Tengo la API REST que expone un recurso grande complejo y quiero poder clonar este recurso. Suponga que el recurso está expuesto en /resources/{resoureId}
Para clonar el recurso 10 podría hacer algo como.
-
GET /resources/10
-
POST /resources/
body of put que contiene un duplicado de la representación porGET /resources/10
sin el id. Para que elPOST
cree un nuevo recurso.
El problema con este enfoque es que el recurso es muy grande y complejo, realmente no tiene sentido devolver una representación completa al cliente y luego hacer que el cliente lo envíe de vuelta ya que eso sería solo una pérdida total de ancho de banda, y la CPU en el servidor. Clonar el recurso en el servidor es mucho más fácil, así que quiero hacer eso.
Podría hacer algo como POST /resources/10/clone
o POST resources/clone/10
pero ambos enfoques se sienten mal porque el verbo en la URL.
¿Cuál es la forma más "tranquila / ingeniosa" de construir url que se puede usar en este tipo de situaciones?
La respuesta de Francis es excelente y probablemente sea lo que estás buscando. Dicho esto, no es técnicamente RESTful ya que (como dice en los comentarios) se basa en que el cliente proporcione información fuera de banda. Dado que la pregunta era "¿cuál es el camino de descanso" y no "cuál es la mejor manera / la mejor manera", eso me hizo pensar si existe una solución REST. Y creo que lo que sigue es una solución REST, aunque no estoy seguro de que sea necesariamente mejor en la práctica.
En primer lugar, como ya has identificado, GET seguido de POST es la forma RESTful simple y obvia, pero no es eficiente. Así que estamos buscando una optimización, ¡y no deberíamos sorprendernos si se siente un poco menos natural que esa solución!
La solución POST + sourceId crea una URL especial, una que apunta no a un recurso, sino a una instrucción para hacer algo. Cada vez que se encuentre creando URL especiales como esa, vale la pena considerar si puede evitar la necesidad de hacerlo simplemente definiendo más recursos.
Queremos la capacidad de copiar
resources/10
¿Qué pasa si encontramos otro recurso?
resources/10/copies
... y la definición de este recurso es simplemente "la colección de recursos que son copias de recursos / 10".
Con este recurso definido, ahora podemos volver a establecer nuestra operación de copia en diferentes términos: en lugar de decir "Quiero que el servidor copie recursos / 10", podemos decir "Quiero agregar algo nuevo a la colección de cosas que son copias de recursos / 10 ".
Esto suena extraño, pero encaja naturalmente en la semántica de REST. Por ejemplo, digamos que este recurso actualmente se ve así (voy a usar una representación JSON aquí):
[]
Podemos actualizar eso con un POST o PATCH [1]:
POST resources/copies/10
["resources/11"]
Tenga en cuenta que todo lo que enviamos al servidor son metadatos sobre una colección, por lo que es muy eficiente. Podemos suponer que el servidor ahora sabe dónde obtener los datos para copiar, ya que eso es parte de la definición de este recurso. También podemos suponer que el cliente sabe que esto da como resultado la creación de un nuevo recurso en "resources / 11" por la misma razón.
Con esta solución, todo se define claramente como un recurso, y todo tiene una URL canónica, y el cliente no necesita ninguna información fuera de banda.
En última instancia, ¿vale la pena ir con esta solución de sentimiento extraño solo por el bien de ser más RESTful? Eso probablemente depende de tu proyecto individual. ¡Pero siempre es interesante tratar de enmarcar el problema de manera diferente creando diferentes recursos!
[1] No sé si tiene sentido permitir GET en "resources / 10 / copies". Obviamente, tan pronto como el recurso original o una copia del mismo cambien, la copia ya no es una copia y no debería estar en esta colección. En lo que respecta a la implementación, no veo el sentido de sobrecargar al servidor para que no pierda de vista eso, así que creo que esto debería tratarse como un recurso de solo actualización.
Solo lo pondré ahí, si esto puede ser de ayuda para cualquiera.
Tuvimos un escenario similar, donde proporcionamos "clone vm" como una característica para ampliar nuestra oferta de IaaS. Por lo tanto, si un usuario deseara escalar, tendría que pulsar POST: /vms/vm101
endpoint con request_body siendo
{"action": "clone", // Specifies action to take, since our users can do couple of other actions on a vm, like power_off/power_on etc.
"body": {"name": [vm102, vm103, vm104] // Number of clones to make
"storage": 50, ... // Optional parameters for specifying differences in specs one would want from the base virtual machine
}
y 3 clones de vm101 a saber. vm102, vm103 y vm104 se girarán.