Estoy escribiendo un servicio RESTful para un sistema de gestión de clientes y estoy tratando de encontrar la mejor práctica para actualizar registros parcialmente. Por ejemplo, quiero que la persona que llama pueda leer el registro completo con una solicitud GET. Pero para actualizarlo solo se permiten ciertas operaciones en el registro, como cambiar el estado de ENABLED a DISABLED. (Tengo escenarios más complejos que esto)
No quiero que la persona que llama envíe el registro completo solo con el campo actualizado por razones de seguridad (también parece excesivo).
¿Hay alguna forma recomendada de construir los URI? Al leer los libros REST, las llamadas de estilo RPC parecen estar mal vistas.
Si la siguiente llamada devuelve el registro completo del cliente para el cliente con la identificación 123
GET /customer/123
<customer>
{lots of attributes}
<status>ENABLED</status>
{even more attributes}
</customer>
¿Cómo debo actualizar el estado?
POST /customer/123/status
<status>DISABLED</status>
POST /customer/123/changeStatus
DISABLED
...
Actualización : para aumentar la pregunta. ¿Cómo se incorporan las 'llamadas de lógica de negocios' en una API REST? ¿Hay una forma acordada de hacer esto? No todos los métodos son CRUDOS por naturaleza. Algunos son más complejos, como ' sendEmailToCustomer (123) ', ' mergeCustomers (123, 456) ', ' countCustomers () '
POST /customer/123?cmd=sendEmail
POST /cmd/sendEmail?customerId=123
GET /customer/count
POST
propio Roy Fielding: roy.gbiv.com/untangled/2009/it-is-okay-to-use-post donde la idea básica es: si no hay Es un método (comoGET
oPUT
) ideal para su uso operativoPOST
.Respuestas:
Básicamente tienes dos opciones:
Uso
PATCH
(pero tenga en cuenta que debe definir su propio tipo de medio que especifique qué sucederá exactamente)Úselo
POST
para un recurso secundario y devuelva 303 Vea Otro con el encabezado Ubicación que apunta al recurso principal. La intención del 303 es decirle al cliente: "He realizado su POST y el efecto fue que se actualizó algún otro recurso. Consulte el encabezado de ubicación para saber qué recurso fue". POST / 303 está destinado a adiciones iterativas a recursos para construir el estado de algún recurso principal y es perfecto para actualizaciones parciales.fuente
Debe usar POST para actualizaciones parciales.
Para actualizar los campos para el cliente 123, realice una POST a / customer / 123.
Si desea actualizar solo el estado, también puede PONER en / customer / 123 / status.
En general, las solicitudes GET no deberían tener ningún efecto secundario, y PUT es para escribir / reemplazar todo el recurso.
Esto se sigue directamente de HTTP, como se ve aquí: http://en.wikipedia.org/wiki/HTTP_PUT#Request_methods
fuente
/customer/123
debería crear lo obvio que está lógicamente en el cliente 123. ¿Tal vez un pedido? PUT to/customer/123/status
parece tener más sentido, suponiendo que POST haya/customers
creado implícitamente unstatus
(y suponiendo que sea REST legítimo).POST
no necesitan ser no idempotentes. Y como se mencionó,PUT
debe reemplazar un recurso completo.Debe usar PATCH para actualizaciones parciales, ya sea usando documentos json-patch (consulte http://tools.ietf.org/html/draft-ietf-appsawg-json-patch-08 o http://www.mnot.net/ blog / 2012/09/05 / patch ) o el marco de parches XML (consulte http://tools.ietf.org/html/rfc5261 ). Sin embargo, en mi opinión, json-patch es la mejor opción para su tipo de datos comerciales.
PATCH con documentos de parche JSON / XML tiene una semántica muy estricta para actualizaciones parciales. Si comienza a usar POST, con copias modificadas del documento original, para actualizaciones parciales, pronto se encontrará con problemas en los que desea que falten valores (o, más bien, valores nulos) para representar "ignorar esta propiedad" o "establecer esta propiedad en valor vacío ", y eso lleva a un agujero de conejo de soluciones pirateadas que al final dará como resultado su propio tipo de formato de parche.
Puede encontrar una respuesta más detallada aquí: http://soabits.blogspot.dk/2013/01/http-put-patch-or-post-partial-updates.html .
fuente
Me encuentro con un problema similar. PUT en un recurso secundario parece funcionar cuando desea actualizar un solo campo. Sin embargo, a veces desea actualizar un montón de cosas: piense en un formulario web que represente el recurso con la opción de cambiar algunas entradas. El envío del formulario por parte del usuario no debe dar lugar a múltiples PUT.
Aquí hay dos soluciones en las que puedo pensar:
hacer un PUT con todo el recurso. En el lado del servidor, defina la semántica de que un PUT con todo el recurso ignora todos los valores que no han cambiado.
hacer un PUT con un recurso parcial. En el lado del servidor, defina la semántica de esto como una fusión.
2 es solo una optimización de ancho de banda de 1. A veces, 1 es la única opción si el recurso define que algunos campos son campos obligatorios (piense en los buffers proto).
El problema con estos dos enfoques es cómo limpiar un campo. Tendrá que definir un valor nulo especial (especialmente para los buffers de proto ya que los valores nulos no están definidos para los buffers de proto) que provocarán la eliminación del campo.
Comentarios?
fuente
Para modificar el estado, creo que un enfoque RESTful es usar un subsubcurso lógico que describa el estado de los recursos. Esta IMO es bastante útil y limpia cuando tienes un conjunto reducido de estados. Hace que su API sea más expresiva sin forzar las operaciones existentes para el recurso de su cliente.
Ejemplo:
El servicio POST debe devolver al cliente recién creado con la identificación:
El GET para el recurso creado usaría la ubicación del recurso:
A GET / customer / 123 / inactive debería devolver 404
Para la operación PUT, sin proporcionar una entidad Json, solo actualizará el estado
Proporcionar una entidad le permitirá actualizar el contenido del cliente y actualizar el estado al mismo tiempo.
Está creando un recurso secundario conceptual para el recurso de su cliente. También es consistente con la definición de Roy Fielding de un recurso: "... Un recurso es un mapeo conceptual a un conjunto de entidades, no la entidad que corresponde al mapeo en un punto particular en el tiempo ..." En este caso, el el mapeo conceptual es activo de cliente a cliente con estado = ACTIVO.
Operación de lectura:
Si realiza esas llamadas una después de que la otra debe devolver el estado 404, la salida exitosa puede no incluir el estado, ya que está implícito. Por supuesto, aún puede usar GET / customer / 123? Status = ACTIVE | INACTIVE para consultar el recurso del cliente directamente.
La operación DELETE es interesante ya que la semántica puede ser confusa. Pero tiene la opción de no publicar esa operación para este recurso conceptual, o usarla de acuerdo con su lógica empresarial.
Ese puede llevar a su cliente a un estado ELIMINADO / DESACTIVADO o al estado opuesto (ACTIVO / INACTIVO).
fuente
Cosas para agregar a su pregunta aumentada. Creo que a menudo puedes diseñar perfectamente acciones comerciales más complicadas. Pero tiene que regalar el estilo de pensamiento método / procedimiento y pensar más en recursos y verbos.
envíos de correo
La implementación de este recurso + POST luego enviaría el correo. si es necesario, puede ofrecer algo como / customer / 123 / outbox y luego ofrecer enlaces de recursos a / customer / mails / {mailId}.
recuento de clientes
Puede manejarlo como un recurso de búsqueda (incluidos los metadatos de búsqueda con paginación e información numérica, lo que le da el recuento de clientes).
fuente
Use PUT para actualizar recursos incompletos / parciales.
Puede aceptar jObject como parámetro y analizar su valor para actualizar el recurso.
A continuación se muestra la función que puede usar como referencia:
fuente
En cuanto a su actualización.
El concepto de CRUD creo que ha causado cierta confusión con respecto al diseño de API. CRUD es un concepto general de bajo nivel para que las operaciones básicas se realicen en datos, y los verbos HTTP son solo métodos de solicitud ( creados hace 21 años ) que pueden o no asignarse a una operación CRUD. De hecho, intente encontrar la presencia del acrónimo CRUD en la especificación HTTP 1.0 / 1.1.
Puede encontrar una guía muy bien explicada que aplica una convención pragmática en la documentación de la API de la plataforma en la nube de Google . Describe los conceptos detrás de la creación de una API basada en recursos, que enfatiza una gran cantidad de recursos sobre las operaciones, e incluye los casos de uso que está describiendo. Aunque es solo un diseño de convención para su producto, creo que tiene mucho sentido.
El concepto base aquí (y uno que produce mucha confusión) es el mapeo entre "métodos" y verbos HTTP. Una cosa es definir qué "operaciones" (métodos) hará su API sobre qué tipos de recursos (por ejemplo, obtener una lista de clientes o enviar un correo electrónico), y otra son los verbos HTTP. Debe haber una definición de ambos, los métodos y los verbos que planea usar y un mapeo entre ellos .
También dice que, cuando una operación no se corresponde exactamente con un método estándar (
List
,Get
,Create
,Update
,Delete
en este caso), se puede utilizar "métodos personalizados", al igualBatchGet
, que recupera varios objetos en función de varios de entrada de objeto de identificación, oSendEmail
.fuente
RFC 7396 : parche de fusión JSON (publicado cuatro años después de la publicación de la pregunta) describe las mejores prácticas para un PARCHE en términos de formato y reglas de procesamiento.
En pocas palabras, envía un PATCH HTTP a un recurso de destino con el tipo de medio MIME application / merge-patch + json y un cuerpo que representa solo las partes que desea cambiar / agregar / eliminar y luego sigue las siguientes reglas de procesamiento.
reglas :
Ejemplos de casos de prueba que ilustran las reglas anteriores (como se ve en el apéndice de ese RFC):
fuente
Echa un vistazo a http://www.odata.org/
Define el método MERGE, por lo que en su caso sería algo como esto:
Solo
status
se actualiza la propiedad y se conservan los demás valores.fuente
MERGE
un verbo HTTP válido?MERGE was used to do PATCH before PATCH existed. Now that we have PATCH, we no longer need MERGE.
Ver docs.oasis-open.org/odata/new-in-odata/v4.0/cn01/…No importa. En términos de REST, no puede hacer un GET, porque no se puede almacenar en caché, pero no importa si usa POST o PATCH o PUT o lo que sea, y no importa cómo se vea la URL. Si está haciendo REST, lo que importa es que cuando obtiene una representación de su recurso del servidor, esa representación puede brindarle al cliente opciones de transición de estado.
Si su respuesta GET tenía transiciones de estado, el cliente solo necesita saber cómo leerlas y el servidor puede cambiarlas si es necesario. Aquí se realiza una actualización mediante POST, pero si se cambió a PATCH, o si la URL cambia, el cliente aún sabe cómo realizar una actualización:
Podría ir tan lejos como para enumerar los parámetros obligatorios / opcionales para que el cliente le devuelva. Depende de la aplicación.
En cuanto a las operaciones comerciales, ese podría ser un recurso diferente vinculado al recurso del cliente. Si desea enviar un correo electrónico al cliente, tal vez ese servicio es un recurso propio en el que puede PUBLICAR, por lo que puede incluir la siguiente operación en el recurso del cliente:
Algunos buenos videos, y ejemplos de la arquitectura REST del presentador son estos. Stormpath solo usa GET / POST / DELETE, lo cual está bien ya que REST no tiene nada que ver con las operaciones que usa o cómo deben verse las URL (excepto que las GET deben ser almacenables en caché):
https://www.youtube.com/watch?v=pspy1H6A3FM ,
https://www.youtube.com/watch?v=5WXYw4J4QOU ,
http://docs.stormpath.com/rest/quickstart/
fuente