Evite los POST duplicados con REST
atomicity (6)
Identificadores emitidos por el servidor
Si se trata del caso en el que es el servidor el que emite los identificadores, cree el objeto en un estado temporal y por etapas. (Esta es una operación intrínsecamente no idempotente, por lo que debe hacerse con POST.) Luego, el cliente tiene que realizar otra operación para transferirla del estado por etapas al estado activo / conservado (que podría ser un PUT de una propiedad del recurso, o un POST adecuado para el recurso).
Cada cliente debería poder OBTENER una lista de sus recursos en el estado por etapas de alguna manera (tal vez mezclado con otros recursos) y debería poder BORRAR los recursos que haya creado si todavía están en etapas. También puede eliminar periódicamente recursos escalonados que han estado inactivos durante algún tiempo.
No necesita revelar los recursos escalonados de un cliente a ningún otro cliente; necesitan existir globalmente solo después del paso confirmatorio.
Identificadores emitidos por el cliente
La alternativa es que el cliente emita los identificadores. Esto es útil principalmente cuando está modelando algo así como una tienda de archivos, ya que los nombres de los archivos son generalmente significativos para el código de usuario. En este caso, puede usar PUT para hacer la creación del recurso, ya que puede hacerlo todo de forma idempotente.
La desventaja de esto es que los clientes pueden crear ID, por lo que no tiene ningún control sobre qué ID utilizan.
He estado usando POST en una API REST para crear objetos. De vez en cuando, el servidor crea el objeto, pero el cliente se desconectará antes de recibir la respuesta 201 Created
. El cliente solo ve una solicitud POST fallida e intenta nuevamente más tarde, y el servidor crea felizmente un objeto duplicado ...
Otros deben haber tenido este problema, ¿verdad? Pero busco en Google y todos parecen ignorarlo.
Tengo 2 soluciones:
A) Use PUT en su lugar, y cree la ID (GU) en el cliente.
B) Agregue un GUID a todos los objetos creados en el cliente, y haga que el servidor aplique su UNIQUE
-ness.
A no coincide muy bien con los marcos existentes, y B se siente como un hack. ¿Cómo lo resuelven otras personas, en el mundo real?
Editar:
Con Backbone.js, puede establecer un GUID como ID cuando crea un objeto en el cliente. Cuando se guarda, Backbone hará una solicitud PUT. Haga que su manejador de fondo REST PONGA en identificadores no existentes, y usted está listo.
Hay otra variación de este problema. Hacer que un cliente genere una identificación única indica que le pedimos a un cliente que resuelva este problema para nosotros. Considere un entorno en el que tenemos API expuestas públicamente y cientos de clientes que se integran con estas API. Prácticamente, no tenemos control sobre el código del cliente y la corrección de su implementación de exclusividad. Por lo tanto, probablemente sería mejor tener inteligencia para entender si una solicitud es un duplicado. Un enfoque simple aquí sería calcular y almacenar la suma de verificación de cada solicitud en función de los atributos de una entrada de usuario, definir algún umbral de tiempo (x minutos) y comparar cada nueva solicitud del mismo cliente con las recibidas en x minutos pasados. Si la suma de comprobación coincide, podría ser una solicitud duplicada y agregar algún mecanismo de desafío para que un cliente resuelva esto. Si un cliente realiza dos solicitudes diferentes con los mismos parámetros dentro de x minutos, podría valer la pena garantizar que esto sea intencional, incluso si viene con un ID de solicitud único. Este enfoque puede no ser adecuado para todos los casos de uso, sin embargo, creo que esto será útil para los casos en que el impacto comercial de ejecutar la segunda llamada sea alto y pueda potencialmente costarle a un cliente. Considere una situación de motor de procesamiento de pagos donde una capa intermedia termina reintentando una solicitud fallida O un cliente hace doble clic, lo que resulta en el envío de dos solicitudes por capa de cliente.
La detección de duplicados es un problema, y puede ser muy complicado. Las solicitudes genuinas distintas pero similares pueden llegar al mismo tiempo, tal vez porque se restaura una conexión de red. Y las solicitudes de repetición pueden llegar horas o días de diferencia si se desconecta una conexión de red.
Toda la discusión de los identificadores en los otros informes es con el objetivo de dar un error en respuesta a las solicitudes duplicadas, pero esto normalmente solo incitará a un cliente a obtener o generar una nueva identificación y volver a intentarlo.
Un patrón simple y robusto para resolver este problema es el siguiente: las aplicaciones de servidor deben almacenar todas las respuestas a las solicitudes inseguras, luego, si ven una solicitud duplicada, pueden repetir la respuesta anterior y no hacer nada más . Haga esto para todas las solicitudes inseguras y resolverá un montón de problemas espinosos. "Duplicar" está determinado por un id. De nivel de aplicación, ya sea un GUID generado por el cliente o un número de secuencia generado por el servidor. En este segundo caso, una solicitud-respuesta debe dedicarse solo para intercambiar la identificación. Me gusta esta solución porque el paso dedicado hace que los clientes piensen que están obteniendo algo precioso que deben cuidar. Si pueden generar sus propios identificadores, es más probable que pongan esta línea dentro del ciclo y cada solicitud sangrienta tendrá una nueva identificación.
Usando este esquema, todos los POST están vacíos, y POST se usa solo para recuperar un identificador de acción. Todos los PUT y DELETE son completamente idempotentes: las solicitudes sucesivas obtienen la misma respuesta (almacenada y reproducida) y no hacen que pase nada más. Lo mejor de este patrón es su calidad de Kung-Fu (Panda). Se necesita una debilidad: la propensión de los clientes a repetir una solicitud cada vez que obtienen una respuesta inesperada, y la convierte en una fuerza :-)
Tengo un pequeño documento de Google here si a alguien le importa.
Otra solución que se ha propuesto para esto es POST Once Exactamente (POE) , en la cual el servidor genera un URI POST de un solo uso que, cuando se usa más de una vez, hará que el servidor devuelva una respuesta 405.
Las desventajas son que 1) el borrador del POE expiró sin ningún progreso adicional en la estandarización, y por lo tanto 2) implementarlo requiere cambios a los clientes para hacer uso de los nuevos encabezados de POE y trabajo adicional por parte de los servidores para implementar la semántica de POE.
Sin embargo, al buscar en Google puedes encontrar algunas API que lo están usando.
Otra idea que tuve para resolver este problema es la de un POST condicional, que describí y solicité comentarios here .
Parece que no hay consenso sobre la mejor manera de evitar la creación de recursos duplicados en los casos en que la generación única de URI no puede ser PUT en el cliente y, por lo tanto, se necesita POST.
Podría intentar un enfoque de dos pasos. Solicita que se cree un objeto, que devuelve un token. Luego, en una segunda solicitud, solicite un estado con el token. Hasta que se solicite el estado con el token, lo deja en un estado "en etapas".
Si el cliente se desconecta después de la primera solicitud, no tendrá el token y el objeto se mantendrá "en etapas" indefinidamente o hasta que lo elimine con otro proceso.
Si la primera solicitud tiene éxito, tiene un token válido y puede tomar el objeto creado tantas veces como desee sin que se reproduzca nada.
No hay ninguna razón por la cual el token no puede ser la identificación del objeto en el almacén de datos. Puede crear el objeto durante la primera solicitud. La segunda solicitud realmente solo actualiza el campo "en etapas".
Siempre uso B - detección de dups debido a cualquier problema que pertenezca al lado del servidor.