Consecuencias de POST no ser idempotente (RESTful API)


Me pregunto si mi enfoque actual tiene sentido o si hay una mejor manera de hacerlo.

Tengo varias situaciones en las que quiero crear nuevos objetos y dejar que el servidor asigne un ID a esos objetos. Enviar una solicitud POST parece ser la forma más adecuada de hacerlo. Sin embargo, dado que POST no es idempotente, la solicitud puede perderse y enviarla de nuevo puede crear un segundo objeto. También las solicitudes que se pierden pueden ser bastante comunes, ya que a menudo se accede a la API a través de dispositivos móviles red.

Como resultado, decidí dividir todo en un proceso de dos pasos. Primero se envía una solicitud POST para crear un nuevo objeto que devuelve el URI del nuevo objeto en el encabezado de ubicación. En segundo lugar, realizar una solicitud PUT idempotent a la ubicación suministrada para rellenar el nuevo objeto con datos. Si un nuevo objeto no se rellena dentro de las 24 horas, el servidor puede eliminarlo a través de algún tipo de trabajo por lotes.

¿Eso suena razonable o hay una mejor ¿acercarnos?

Gracias

Author: mibollma, 2012-12-21

6 answers

La única ventaja de la POST-creación sobre la PUT-creación es la generación de ID del servidor. No creo que valga la falta de idempotencia (y luego la necesidad de eliminar duplicados u objetos vacíos).

En su lugar, usaría un PUT con un UUID en la URL. Debido a los generadores de UUID, está casi seguro de que el ID que genera en el lado del cliente será único en el lado del servidor.

 32
Author: Aurélien,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-12-28 13:24:56

Bueno, todo depende, para empezar debes hablar más sobre URI, recursos y representaciones y no preocuparte por objetos.

El método POST está diseñado para solicitudes no idempotentes, o solicitudes con efectos secundarios, pero se puede usar para solicitudes idempotentes.

On POST of form data to / some_collection /

normalize the natural key of your data (Eg. "lowercase" the Title field for a blog post)
calculate a suitable hash value (Eg. simplest case is your normalized field value)
lookup resource by hash value
if none then
    generate a server identity, create resource
        Respond =>  "201 Created", "Location": "/some_collection/<new_id>" 
if found but no updates should be carried out due to app logic
        Respond => 302 Found/Moved Temporarily or 303 See Other 
        (client will need to GET that resource which might include fields required for updates, like version_numbers)
if found but updates may occur
   Respond => 307 Moved Temporarily, Location: /some_collection/<id> 
   (like a 302, but the client should use original http method and might do automatically) 

Una función hash adecuada puede ser tan simple como algunos campos concatenados, o para campos grandes o valores un md5 truncado función podría ser utilizado. Ver [función hash] para más detalles2.

He asumido que:

  • necesita un valor de identidad diferente que un valor hash
  • campos de datos utilizados porque la identidad no se puede cambiar
 14
Author: bdargan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-02 12:57:48

Su método de generar ID en el servidor, en la aplicación, en una solicitud-respuesta dedicada, es muy bueno! La singularidad es muy importante, pero los clientes, como los pretendientes, van a seguir repitiendo la solicitud hasta que tengan éxito, o hasta que obtengan un fracaso que estén dispuestos a aceptar (improbable). Por lo tanto, necesita obtener singularidad de algún lugar, y solo tiene dos opciones. Ya sea el cliente, con un GUID como sugiere Aurélien, o el servidor, como usted sugiere. Resulta que me gusta el servidor opcion. Las columnas semilla en DBs relacional son una fuente de singularidad fácilmente disponible con cero riesgo de colisiones. Alrededor del 2000, leí un artículo que abogaba por esta solución llamada algo así como "Mensajería Confiable Simple con HTTP", por lo que este es un enfoque establecido para un problema real.

Leyendo cosas de DESCANSO, podrías ser perdonado por pensar que un grupo de adolescentes acaban de heredar la mansión de Elvis. Están emocionados discutiendo cómo reorganizar los muebles, y están histéricos en el idea de que podrían necesitar traer algo de casa. Se recomienda el uso de POST porque está ahí, sin abordar los problemas con las solicitudes no idempotentes.

En la práctica, es probable que desee asegurarse de que todas las solicitudes inseguras a su api sean idempotentes, con la excepción necesaria de las solicitudes de generación de identidad, que, como señala, no importan. Generar identidades es barato y las que no se usan se descartan fácilmente. Como un guiño al DESCANSO, recuerde obtener tu nueva identidad con una PUBLICACIÓN, por lo que no se almacena en caché y se repite en todo el lugar.

Respecto a el debate estéril sobre lo que significa idempotente, yo digo que tiene que ser todo. Las solicitudes sucesivas no deberían generar efectos adicionales y deberían recibir la misma respuesta que la primera solicitud procesada. Para implementar esto, querrá almacenar todas las respuestas del servidor para que se puedan reproducir, y sus ID identificarán acciones, no solo recursos. Te echarán. La mansión de Elvis, pero tendrás una api a prueba de bombas.

 4
Author: bbsimonbb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-11 08:14:11

Pero ahora usted tiene dos peticiones que se pueden perder? Y el POST todavía se puede repetir, creando otra instancia de recurso. No pienses demasiado. Simplemente haga que el proceso por lotes busque duplicados. Posiblemente tenga algunas estadísticas de conteo de "acceso" en sus recursos para ver cuál de los candidatos engañados fue el resultado de un post abandonado.

Otro enfoque: filtrar los MENSAJES entrantes contra algún registro para ver si se trata de una repetición. Debe ser fácil de encontrar: si el contenido del cuerpo de una igual que la de una solicitud hace solo x tiempo, considéralo una repetición. Y usted podría comprobar parámetros adicionales como la IP de origen, misma autenticación, ...

 3
Author: Marjan Venema,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-12-21 14:52:06

No importa qué método HTTP use, es teóricamente imposible hacer una solicitud idempotente sin generar el identificador único del lado del cliente, temporalmente (como parte de algún sistema de verificación de solicitudes) o como el id del servidor permanente. Una solicitud HTTP que se pierde no creará un duplicado, aunque existe la preocupación de que la solicitud podría tener éxito al llegar al servidor, pero la respuesta no lo hace de nuevo al cliente.

Si el cliente final puede eliminar fácilmente los duplicados y no cause conflictos de datos inherentes probablemente no sea lo suficientemente grande como para desarrollar un sistema de prevención de duplicación ad-hoc. Use POST para la solicitud y envíe al cliente un estado 201 en el encabezado HTTP y el id único generado por el servidor en el cuerpo de la respuesta. Si tiene datos que muestran que las duplicaciones son una ocurrencia frecuente o cualquier duplicado causa problemas significativos, usaría PUT y crearía el id único del lado del cliente. Utilice el id creado por el cliente como el id de la base de datos - no hay ventaja de crear un id único adicional en el servidor.

 3
Author: Chris Broski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-12-31 19:26:08

Creo que también podría colapsar la solicitud de creación y actualización en una sola solicitud (upsert). Para crear un nuevo recurso, el cliente PUBLICA un recurso "factory", ubicado, por ejemplo, en /factory-url-name. Y luego el servidor devuelve el URI para el nuevo recurso.

 2
Author: freedev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-12-21 14:37:07