rest transactions crud atomic multipartform-data

rest - ¿Actualización atómica de recursos múltiples?



transactions crud (4)

Desea utilizar la segunda opción, la opción de transacción.

Lo que te falta es la creación de la transacción:

POST /transaction HTTP/1.1 301 Moved Permanently Location: /transaction/1234

Ahora tienes un recurso de transacción que es un ciudadano de primera clase. Puede agregarlo, eliminarlo, consultar para ver su contenido actual y finalmente confirmar o eliminar (es decir, revertir) la transacción.

Mientras la transacción está en curso, es solo otro recurso. No hay estado del cliente aquí. Cualquiera puede agregar a esta transacción.

Cuando todo está listo, el servidor aplica todos los cambios a la vez utilizando algún mecanismo de transacción interno que está fuera de alcance aquí.

Puede capturar elementos como Etags y encabezados modificados si en las subacciones de transacciones para que cuando se apliquen, sepa que algo no cambió detrás de su espalda.

Imagine una aplicación web que almacena algunos recursos de datos con algún id que almacena tres archivos adjuntos (por ejemplo, pdf) por dato.

El esquema de URL es

data/{id}/attachment1 data/{id}/attachment2 data/{id}/attachment3

Existe una API RESTful para los archivos adjuntos que proporcionan operaciones GET / PUT / DELETE que implementan operaciones CRUD en el lado del servidor.

Dejando que el id sea 123, me gustaría realizar una operación donde

  • attachment1 se reemplaza por un nuevo archivo adjunto (de manera que el GET file/123/attachment1 devuelve el nuevo archivo adjunto)
  • attachment2 se elimina (de modo que el GET file/123/attachment2 devuelve 404)
  • attachment3 se mantiene sin cambios.

La actualización debe ser atómica : la actualización completa es realizada por el servidor o nada en absoluto.

La aplicación de un simple PUT file/123/attachment1 y DELETE file/123/attachment2 no es atómica, ya que el cliente podría fallar después de la PUT y el servidor no tiene ninguna pista de que deba hacer una reversión en este caso.

Entonces, ¿cómo implemento la operación de una manera REST?

He pensado en dos soluciones, pero ambas no parecen ser 100% RESTful:

  • Use PATCH (podría ser PUT, pero PATCH refleja mejor la semántica de una actualización parcial) con multipart / datos de formulario en datos / 123: El multiparte / datos de formulario es una secuencia de entidades que consiste en una nueva "aplicación / pdf" asociada con el campo "attachment1" y algo que representaría un valor nulo para denotar la eliminación de attachment2.

Si bien esto garantiza la atomicidad, dudo que esto sea REST. Ya que sobrecargo el método PATCH utilizando diferentes listas de parámetros, lo que viola la restricción de interfaz uniforme.

  • Utilice un recurso que representa una transacción. Podría enviar el ID de datos 123 a una URL de transacción que crearía un recurso de transacción que representa una copia del estado actual del recurso de datos almacenado en el servidor, por ejemplo, transacción / datos / 123. Ahora puedo llamar a PUT y DELETE en los archivos adjuntos de este recurso temporal (por ejemplo, DELETE transaction/data/123/attachment2 ) y comunicar el compromiso de esta versión del recurso al servidor a través de un PUT en la transacción / data / 123. Esto asegura la atomicidad, mientras que tiene que implementar una lógica adicional del lado del servidor para tratar con varios clientes que cambian el mismo recurso y clientes bloqueados que nunca se comprometieron.

Si bien esto parece ser consistente con REST, parece violar el contraintento de la apatridia. El estado del recurso transaccional no es el estado del servicio sino el estado de la aplicación, ya que cada recurso transaccional está asociado con un solo cliente.

Estoy un poco atascado aquí, así que cualquier idea sería útil, ¡gracias!


No tengo experiencia, pero tengo una idea para una solución ya que enfrento exactamente este problema en desarrollo.

En primer lugar, utilizo mi analogía (cliente) enviando un mensaje a Fred1 en una casa (servidor con recursos) para que apague el interruptor de la luz (cambiar el estado de parte de un recurso) y encienda el hervidor (cambiar Estado de otra parte del recurso). Después de apagar el interruptor de la luz, Fred, desafortunadamente, tiene un ataque al corazón.

Ahora no tengo nada de parte de Fred para decir si hizo o no lo que le pedí. Fred es reemplazado por otro Fred. El mensaje que envié no ha recibido respuesta. La única manera de proceder es preguntarle a Fred2 si el interruptor de la luz está apagado y el hervidor está encendido (el recurso está en el estado que esperaría después de que le pidiera que hiciera cosas por mí). Este es un desafortunado estado de cosas (error) y aumenta mi carga de trabajo, pero ahora puedo continuar sobre la base de que sé lo que hizo Fred1 antes de su ataque al corazón. Puedo volver al tablero de dibujo (informar al usuario que algo salió mal y debemos volver a hacerlo) o hacer los cambios que completarían mi solicitud si eso sigue siendo relevante (encienda el hervidor).

Este es el comienzo de cómo lo haría, obviamente hay una preocupación sobre el alcance, pero si ya he definido mi alcance (solo me interesa el interruptor de la luz y el hervidor), debería tener suficiente información (sabiendo el estado). del interruptor de luz y el hervidor) para dar un nuevo comando a Fred2 sin tener que volver al usuario para recibir instrucciones.

¿Como suena eso?


Pregunta muy interesante. Un profesor de CS en la universidad de Lugano (Suiza) escribió algunas diapositivas sobre esta situación:

http://www.slideshare.net/cesare.pautasso/atomic-transactions-for-the-rest-of-us

Sin embargo, no estoy realmente seguro de que la solución que proporciona sea totalmente RESTABLE porque no parece realmente sin estado en el lado del servidor.

Siendo honesto, ya que la transacción en sí está compuesta por varios estados, no creo que pueda haber una solución totalmente RESTABLE para este problema.


Suponiendo que sus URI son jerárquicas:

PUT data/{id} [attachment2,attachment3]

Parte de su problema es que el archivo adjunto 1/2/3 es un identificador terrible. Un índice nunca debe ser parte de sus URI.