Estoy planeando construir RESTfull API pero hay algunas preguntas arquitectónicas que están creando algunos problemas en mi cabeza. Me gustaría evitar agregar lógica de negocio de back-end a los clientes, ya que es difícil mantener la actualización de múltiples plataformas de clientes en tiempo real cuando la lógica de negocios puede cambiar rápidamente.
Digamos que tenemos un artículo como recurso (api / article), ¿cómo debemos implementar acciones como publicar, anular la publicación, activar o desactivar, etc., pero tratar de mantenerlo lo más simple posible?
1) ¿Deberíamos usar api / article / {id} / {action} ya que puede ocurrir una gran cantidad de lógica de back-end como empujar a ubicaciones remotas o cambiar múltiples propiedades. Probablemente lo más difícil aquí es que necesitamos enviar todos los datos del artículo a la API para su actualización y no se pudo implementar el trabajo multiusuario. Por ejemplo, el editor podría enviar datos 5 segundos más antiguos y sobrescribir la corrección que otro periodista acaba de hacer hace 2 segundos y no hay forma de que pueda explicar esto a los clientes, ya que aquellos que publican un artículo no están realmente relacionados con la actualización del contenido.
2) Crear un nuevo recurso también puede ser una opción, api / article- {action} / id, pero luego el recurso devuelto no sería article- {action} sino un artículo del que no estoy seguro si esto es correcto. También en el código del lado del servidor, la clase de artículo está manejando el trabajo real en ambos recursos y no estoy seguro de si esto va en contra del pensamiento RESTfull
Cualquier sugerencia es bienvenida.
api/article?action=publish
? Los parámetros de consulta están destinados a casos en los que el estado del recurso depende del 'algoritmo' (o acción) que usted menciona. Por ejemplo,api/articles?sort=asc
es válidoRespuestas:
Encuentro útiles las prácticas descritas aquí :
fuente
/article/123/deactivations
para crear una nueva solicitud de desactivación para el artículo 123, es posible que el servidor no solo desactive el recurso solicitado, sino que en realidad almacene mi solicitud de desactivación para que pueda recuperar su estado más adelante.PUT /gists/:id/star
noPOST /gists/:id/star
?Las operaciones que resultan en cambios importantes de estado y comportamiento en el lado del servidor, como la acción "publicar" que describe, son difíciles de modelar explícitamente en REST. Una solución que a menudo veo es conducir ese comportamiento complejo implícitamente a través de los datos.
Considere ordenar productos a través de una API REST expuesta por un comerciante en línea. Ordenar es una operación compleja. Se empacarán y enviarán varios productos, se le cobrará a su cuenta y recibirá un recibo. Puede cancelar su pedido por un período de tiempo limitado y, por supuesto, existe una garantía completa de devolución de dinero que le permite devolver los productos para obtener un reembolso.
En lugar de una operación de compra compleja, dicha API podría permitirle crear un nuevo recurso, una orden de compra. Al principio, puede realizar las modificaciones que desee: agregar o eliminar productos, cambiar la dirección de envío, elegir otra opción de pago o cancelar su pedido por completo. Puede hacer todo esto porque aún no ha comprado nada, solo está manipulando algunos datos en el servidor.
Una vez que se completa su orden de compra y su período de gracia pasa, el servidor bloquea su orden para evitar más cambios. Solo en este momento comienza la compleja secuencia de operaciones, pero no puede controlarla directamente, solo indirectamente a través de los datos que colocó previamente en la orden de compra.
Según su descripción, "publicar" podría implementarse de esta manera. En lugar de exponer una operación, coloca una copia del borrador que ha revisado y desea publicar como un nuevo recurso en / publicar. Esto garantiza que las actualizaciones posteriores al borrador no se publicarán, incluso si la operación de publicación completa horas después.
fuente
Este tipo de cosas es un desafío, no importa lo que haga, es un problema muy similar al control de fuente distribuida (mercurial, git, etc.), y la solución, escrita en HTTP / ReST, se ve un poco similar.
Supongamos que tienes dos usuarios, Alice y Bob, ambos trabajando
/articles/lunch
. (para mayor claridad, la respuesta está en negrita)Primero, Alice crea el artículo.
El servidor no creó un recurso, porque no había una "versión" adjunta a la solicitud (suponiendo un identificador de
/articles/{id}/{version}
. Para realizar la creación, Alice fue redirigida a la url del artículo / versión que creará. Usuario de Alice El agente volverá a aplicar la solicitud en la nueva dirección.Y ahora el artículo ha sido creado. a continuación, Bob mira el artículo:
Bob mira allí:
Decide agregar su propio cambio.
Al igual que con Alice, Bob es redirigido a donde creará una nueva versión.
Finalmente, Alice decide que le gustaría agregar a su propio artículo:
En lugar de ser redirigido de la forma normal, se devuelve un código de estado diferente al cliente
409
, que le dice a Alice que la versión de la que estaba tratando de ramificarse ya se ha ramificado. Los nuevos recursos se crearon de todos modos (como se muestra en elLocation
encabezado), y las diferencias entre los dos se incluyeron en el cuerpo de respuesta. Alice ahora sabe que la solicitud que acaba de hacer debe fusionarse de alguna manera.Toda esta redirección está relacionada con la semántica de
PUT
, que requiere que se creen nuevos recursos exactamente donde lo solicita la línea de solicitud. esto también podría ahorrar un ciclo de solicitud utilizandoPOST
, pero luego el número de versión tendría que estar codificado en la solicitud por alguna otra magia, lo que me pareció menos obvio para fines ilustrativos, pero probablemente aún sería preferido en una API real para minimizar los ciclos de solicitud / respuesta.fuente
Aquí hay otro ejemplo que no trata el contenido de los documentos, sino más bien el estado transitorio. (Encuentro el control de versiones, dado que, en general, cada versión puede ser un nuevo recurso, un tipo de problema fácil).
Digamos que quiero exponer un servicio que se ejecuta en una máquina a través de un REST para que pueda detenerse, iniciarse, reiniciarse, etc.
¿Cuál es el enfoque más RESTANTE aquí? POST / service? Command = restart, por ejemplo? ¿O POST / service / state con un cuerpo de, por ejemplo, 'corriendo'?
Sería bueno codificar las mejores prácticas aquí y si REST es el enfoque correcto para este tipo de situación.
En segundo lugar, supongamos que quiero impulsar alguna acción desde un servicio que no afecta a su propio estado, sino que desencadena un efecto secundario. Por ejemplo, un servicio de correo que envía un informe, creado en el momento de la llamada, a un montón de direcciones de correo electrónico.
GET / report podría ser una forma de obtener una copia del informe yo mismo; pero ¿qué pasa si queremos impulsar al lado del servidor otras acciones como enviar correos electrónicos como digo anteriormente? O escribiendo en una base de datos.
Estos casos bailan en torno a la división de recursos y acciones, y veo formas de manejarlos de una manera orientada a REST, pero, francamente, se siente como un truco para hacerlo. Quizás la pregunta clave es si una API REST debería admitir efectos secundarios en general.
fuente
REST está orientado a datos y, como tal, los recursos funcionan mejor como "cosas" que como acciones. La semántica implícita de los métodos http; OBTENER, PONER, BORRAR, etc. sirven para reforzar la orientación. POST, por supuesto, es la excepción.
Un recurso puede ser una mezcla de datos, es decir. contenido del artículo; y metadatos, es decir. publicado, bloqueado, revisión. Hay muchas otras formas posibles de dividir los datos, pero primero debe analizar cómo se verá el flujo de datos para determinar cuál es el más óptimo (si lo hay). Por ejemplo, puede ser que las revisiones sean su propio recurso según el artículo, como sugiere TokenMacGuy.
Con respecto a la implementación, probablemente haría algo como lo que sugiere TockenMacGuy. También agregaría campos de metadatos en el artículo, no revisión, como 'bloqueado' y 'publicado'.
fuente
No pienses que está manipulando directamente el estado del artículo. En cambio, está poniendo una orden de cambio solicitando que se cree el artículo.
Puede modelar la colocación de una orden de cambio como la creación de un nuevo recurso de orden de cambio (POST). Hay muchas ventajas. Por ejemplo, puede especificar una fecha y hora futuras en que el artículo debe publicarse como parte de la orden de cambio, y dejar que el servidor se preocupe por cómo se implementa.
Si la publicación no es un proceso instantáneo, no tiene que esperar a que termine antes de volver al cliente. Simplemente reconoce que se creó la orden de cambio y devuelve el ID de la orden de cambio. Luego puede usar la URL correspondiente a esa orden de cambio para compartir el estado de la orden de cambio.
Una idea clave para mí fue reconocer que esta metáfora del orden de cambio es solo otra forma de describir la programación orientada a objetos. En lugar de recursos, llamamos a los objetos. En lugar de órdenes de cambio, los llamamos mensajes. Una forma de enviar un mensaje de A a B en OO es hacer que A llame a un método en B. Otra forma de hacerlo, particularmente cuando A y B están en computadoras diferentes, es hacer que A cree un nuevo objeto, M y envíelo a B. REST simplemente formaliza ese proceso.
fuente
Si te entiendo correctamente, creo que lo que tienes es más una cuestión de determinación de "regla de negocios" que una cuestión técnica.
El hecho de que un artículo se puede sobrescribir podría resolverse mediante la introducción de niveles de autorización en los que los usuarios mayores pueden anular las versiones de los usuarios menores. También mediante la introducción de versiones y una columna para capturar el estado del artículo (por ejemplo, "en desarrollo", "final" , etc.), podrías superar esto. También puede darle al usuario la posibilidad de seleccionar una versión dada, ya sea por una combinación de tiempo de envío y por el número de versión.
En todos los casos anteriores, su servicio debe implementar las reglas comerciales que establezca. Por lo tanto, puede llamar al servicio con los parámetros: ID de usuario, artículo, versión, acción (donde la versión es opcional, nuevamente esto depende de las reglas de su negocio).
fuente