Tenga en cuenta que tengo una comprensión rudimentaria de REST. Digamos que tengo esta URL:
http://api.animals.com/v1/dogs/1/
Y ahora, quiero hacer que el servidor haga ladrar al perro. Solo el servidor sabe cómo hacer esto. Digamos que quiero que se ejecute en un trabajo CRON que hace que el perro ladre cada 10 minutos por el resto de la eternidad. ¿Cómo se ve esa llamada? Como que quiero hacer esto:
Solicitud de URL:
ACTION http://api.animals.com/v1/dogs/1/
En el cuerpo de la solicitud:
{"action":"bark"}
Antes de que te enojes conmigo por haber inventado mi propio método HTTP, ayúdame y dame una mejor idea sobre cómo debería invocar un método del lado del servidor de una manera RESTful. :)
EDITAR PARA ACLARAR
Algunas aclaraciones más sobre lo que hace el método "ladrar". Estas son algunas opciones que pueden dar como resultado llamadas API estructuradas de manera diferente:
- ladrar solo envía un correo electrónico a dog.email y no registra nada.
- ladrar envía un correo electrónico a dog.email y aumenta dog.barkCount en 1.
- ladrar crea un nuevo registro de "ladrido" con la grabación bark.timestamp cuando se produce el ladrido. También incrementa dog.barkCount en 1.
- ladrar ejecuta un comando del sistema para extraer la última versión del código de perro de Github. Luego envía un mensaje de texto a dog.owner diciéndoles que el nuevo código de perro está en producción.
fuente
PATCH
puede ser apropiado. Explico por qué hacia el final de mi respuesta .Respuestas:
¿Por qué apuntar a un diseño RESTful?
Los principios RESTful traen las características que hacen que los sitios web sean fáciles (para que un usuario humano aleatorio los "navegue") al diseño de la API de servicios web , por lo que son fáciles de usar para un programador. REST no es bueno porque es REST, es bueno porque es bueno. Y es bueno sobre todo porque es simple .
La simplicidad de HTTP simple (sin sobres SOAP y
POST
servicios sobrecargados de URI único ), lo que algunos pueden llamar "falta de características" , es en realidad su mayor fortaleza . De inmediato, HTTP le pide que tenga direccionabilidad y apatridia : las dos decisiones de diseño básicas que mantienen a HTTP escalable hasta los mega-sitios (y mega-servicios) actuales.Pero REST no es el bulltet plateado: a veces, un estilo RPC ("Llamada a procedimiento remoto", como SOAP) puede ser apropiado , y otras necesidades tienen prioridad sobre las virtudes de la Web. Esto esta bien. Lo que realmente no nos gusta es la complejidad innecesaria . Con demasiada frecuencia, un programador o una empresa incorporan servicios de estilo RPC para un trabajo que HTTP antiguo podría manejar perfectamente. El efecto es que HTTP se reduce a un protocolo de transporte para una enorme carga útil XML que explica lo que "realmente" está sucediendo (no el URI o el método HTTP dan una pista al respecto). El servicio resultante es demasiado complejo, imposible de depurar y no funcionará a menos que sus clientes tengan la configuración exacta que el desarrollador pretendía.
De la misma manera, un código Java / C # no puede estar orientado a objetos, solo usar HTTP no hace que un diseño RESTful. Uno puede estar atrapado en la prisa de pensar en sus servicios en términos de acciones y métodos remotos que deberían llamarse. No es de extrañar que esto termine principalmente en un servicio de estilo RPC (o un híbrido REST-RPC). El primer paso es pensar de manera diferente. Se puede lograr un diseño RESTful de muchas maneras, una es pensar en su aplicación en términos de recursos, no de acciones:
Iré por los ejemplos a continuación. (Otro aspecto clave de REST es el uso de HATEOAS: no lo cepillo aquí, pero lo hablo rápidamente en otra publicación ).
Problemas del primer diseño
Echemos un vistazo al diseño propuesto:
En primer lugar, no deberíamos considerar crear un nuevo verbo HTTP (
ACTION
). En términos generales, esto no es deseable por varias razones:ACTION
existe verbo?Ahora consideremos usar
POST
(discutiremos por qué a continuación, solo tome mi palabra ahora):Esto podría estar bien ... pero solo si :
{"action":"bark"}
era un documento; y/v1/dogs/1/
era un URI de "procesador de documentos" (similar a una fábrica). Un "procesador de documentos" es un URI en el que simplemente "arrojaría cosas" y "olvidaría" sobre ellos; el procesador puede redirigirlo a un recurso recién creado después del "lanzamiento". Por ejemplo, el URI para publicar mensajes en un servicio de intermediario de mensajes, que, después de la publicación, lo redirigiría a un URI que muestra el estado del procesamiento del mensaje.No sé mucho sobre su sistema, pero apuesto a que ambos no son ciertos:
{"action":"bark"}
no es un documento , en realidad es el método que está tratando de introducir ninja en el servicio; y/v1/dogs/1/
URI representa un recurso "perro" (probablemente el perro conid==1
) y no un procesador de documentos.Entonces, todo lo que sabemos ahora es que el diseño anterior no es tan RESTANTE, pero ¿qué es eso exactamente? ¿Qué tiene de malo? Básicamente, es malo porque es un URI complejo con significados complejos. No se puede inferir nada de eso. ¿Cómo sabría un programador que un perro tiene una
bark
acción que se puede infundir secretamente con un perroPOST
?Diseñando las llamadas a la API de tu pregunta
Así que vamos al grano e intentemos diseñar esos ladridos RESTAMENTE pensando en términos de recursos . Permítame citar el libro de Restful Web Services :
Siguiendo la descripción anterior, podemos ver que
bark
puede modelarse como un subrecurso de adog
(dado que abark
está contenido dentro de un perro, es decir, una corteza es "ladrada" por un perro).De ese razonamiento ya obtuvimos:
POST
/barks
, sub-recurso de dog:, que/v1/dogs/1/barks
representa unabark
"fábrica". Ese URI es único para cada perro (ya que está debajo/v1/dogs/{id}
).Ahora cada caso de su lista tiene un comportamiento específico.
1. ladrar solo envía un correo electrónico a
dog.email
y no registra nada.En primer lugar, ¿ladrar (enviar un correo electrónico) es una tarea síncrona o asíncrona? En segundo lugar, ¿la
bark
solicitud requiere algún documento (el correo electrónico, tal vez) o está vacío?1.1 ladrar envía un correo electrónico a
dog.email
y no registra nada (como una tarea sincrónica)Este caso es simple. Una llamada al
barks
recurso de la fábrica produce un ladrido (un correo electrónico enviado) de inmediato y la respuesta (si está bien o no) se da de inmediato:Como no registra (cambia) nada,
200 OK
es suficiente. Muestra que todo salió como se esperaba.1.2 ladrar envía un correo electrónico
dog.email
y no registra nada (como una tarea asincrónica)En este caso, el cliente debe tener una forma de rastrear la
bark
tarea. Labark
tarea debería ser un recurso con su propio URI .:De esta manera, cada uno
bark
es rastreable. El cliente puede emitir unGET
a labark
URI para saber su estado actual. Tal vez incluso use aDELETE
para cancelarlo.2. ladrar envía un correo electrónico
dog.email
y luego incrementadog.barkCount
en 1Este puede ser más complicado si desea que el cliente sepa que el
dog
recurso ha cambiado:En este caso, la
location
intención del encabezado es hacerle saber al cliente que debe echarle un vistazodog
. Del HTTP RFC sobre303
:Si la tarea es asíncrona,
bark
se necesita un subrecurso al igual que la1.2
situación y303
debe devolverse en elGET .../barks/Y
momento en que se completa la tarea.3. ladrar crea un nuevo "
bark
" registro conbark.timestamp
grabación cuando se produce la corteza. También se incrementadog.barkCount
en 1.Aquí,
bark
se crea debido a la solicitud, por lo que201 Created
se aplica el estado .Si la creación es asíncrona,
202 Accepted
se requiere a (en su lugar, como dice el RFC de HTTP ).La marca de tiempo guardada es parte del
bark
recurso y se puede recuperar con unGET
. El perro actualizado puede ser "documentado" en esoGET dogs/X/barks/Y
también.4. ladrar ejecuta un comando del sistema para extraer la última versión del código de perro de Github. Luego envía un mensaje de texto para
dog.owner
decirles que el nuevo código de perro está en producción.La redacción de este es complicada, pero es una tarea asincrónica simple:
Luego, el cliente emitiría
GET
s para/v1/dogs/1/barks/a65h44
conocer el estado actual (si se extraía el código, se enviaba el correo electrónico al propietario y tal). Cada vez que el perro cambia, a303
es aplicable.Terminando
Citando a Roy Fielding :
En los ejemplos anteriores,
POST
está diseñado uniformemente. Hará al perro "bark
". Eso no es seguro (lo que significa que la corteza tiene efectos sobre los recursos), ni idempotente (cada solicitud produce un nuevobark
), que se ajusta bien alPOST
verbo.Un programador sabría: a
POST
para producirbarks
unbark
. Los códigos de estado de respuesta (también con entidad-cuerpo y encabezados cuando es necesario) hacen el trabajo de explicar qué cambió y cómo el cliente puede y debe proceder.Nota: Las fuentes principales utilizadas fueron: el libro " Restful Web Services ", el HTTP RFC y el blog de Roy Fielding .
Editar:
La pregunta y, por lo tanto, la respuesta han cambiado bastante desde su creación. La pregunta original fue sobre el diseño de un URI como:
A continuación se muestra la explicación de por qué no es una buena opción:
Cómo los clientes le dicen al servidor QUÉ HACER con los datos es la información del método .
EN QUÉ PARTE de los datos [el cliente quiere que el servidor] opere es la información de alcance .
Como ejemplo, tome el URI de Google
http://www.google.com/search?q=DOG
. Allí, la información del método esGET
y la información del alcance es/search?q=DOG
.Larga historia corta:
Y la regla de oro:
Puede poner la " acción" "ladrar" en la URL (o en el cuerpo de la entidad) y usar
POST
. No hay problema, funciona, y puede ser la forma más sencilla de hacerlo, pero esto no es RESTful .Para mantener su servicio realmente RESTANTE, es posible que tenga que dar un paso atrás y pensar qué es lo que realmente quiere hacer aquí (qué efectos tendrá en los recursos).
No puedo hablar sobre sus necesidades comerciales específicas, pero permítame darle un ejemplo: considere un servicio de pedidos RESTful donde los pedidos están en URI como
example.com/order/123
.Ahora digamos que queremos cancelar un pedido, ¿cómo lo haremos? Uno puede estar tentado a pensar que es una "acción" de "cancelación " y diseñarlo como
POST example.com/order/123?do=cancel
.Eso no es RESTful, como hemos dicho anteriormente. En cambio, podríamos
PUT
una nueva representación de laorder
con uncanceled
elemento enviado atrue
:Y eso es. Si el pedido no se puede cancelar, se puede devolver un código de estado específico. (Un diseño de subrecursos, como
POST /order/123/canceled
con el cuerpo de la entidadtrue
puede, por simplicidad, también estar disponible).En su escenario específico, puede intentar algo similar. De esa manera, mientras un perro ladra, por ejemplo, un
GET
at/v1/dogs/1/
podría incluir esa información (por ejemplo<barking>true</barking>
) . O ... si eso es demasiado complicado, afloje su requisito RESTful y siga conPOST
.Actualizar:
No quiero que la respuesta sea demasiado grande, pero me toma un tiempo acostumbrarme a exponer un algoritmo (una acción ) como un conjunto de recursos. En lugar de pensar en términos de acciones ( "hacer una búsqueda de lugares en el mapa" ), uno debe pensar en términos de los resultados de esa acción ( "la lista de lugares en el mapa que coincide con un criterio de búsqueda" ).
Es posible que vuelva a este paso si descubre que su diseño no se ajusta a la interfaz uniforme de HTTP.
Las variables de consulta son información de alcance , pero no denotan nuevos recursos (
/post?lang=en
es claramente el mismo recurso que/post?lang=jp
, solo una representación diferente). Más bien, se utilizan para transmitir el estado del cliente (como?page=10
, por ejemplo , ese estado no se mantiene en el servidor;?lang=en
también es un ejemplo aquí) o parámetros de entrada a recursos algorítmicos (/search?q=dogs
,/dogs?code=1
). De nuevo, no recursos distintos.Propiedades de los verbos HTTP (métodos):
Otro punto claro que se muestra
?action=something
en el URI no es RESTful, son las propiedades de los verbos HTTP:GET
yHEAD
son seguros (e idempotentes);PUT
yDELETE
solo son idempotentes;POST
Es ninguno.Seguridad : Una
GET
oHEAD
solicitud es una solicitud para leer algunos datos, no una solicitud para cambiar cualquier estado del servidor. El cliente puede hacerGET
oHEAD
solicitar 10 veces y es lo mismo que hacerlo una vez, o nunca hacerlo .Idempotencia : una operación idempotente en una que tiene el mismo efecto si la aplicas una o más de una vez (en matemáticas, multiplicar por cero es idempotente). Si tiene
DELETE
un recurso una vez, la eliminación nuevamente tendrá el mismo efecto (el recursoGONE
ya está ).POST
No es seguro ni idempotente. Hacer dosPOST
solicitudes idénticas a un recurso 'de fábrica' probablemente dará como resultado dos recursos subordinados que contengan la misma información. Con sobrecargado (método en URI o entidad-cuerpo)POST
, todas las apuestas están desactivadas.Ambas propiedades fueron importantes para el éxito del protocolo HTTP (a través de redes poco confiables): ¿cuántas veces ha actualizado (
GET
) la página sin esperar hasta que esté completamente cargada?Crear una acción y colocarla en la URL claramente rompe el contrato de los métodos HTTP. Una vez más, la tecnología te permite, puedes hacerlo, pero ese no es un diseño RESTful.
fuente
POST
fue diseñado para "proporcionar un bloque de datos ... a un proceso de manejo de datos" . Parece que mucha gente distingue los recursos de las acciones, pero en realidad las acciones son solo un tipo de recurso.POST
"proporcionar un bloque de datos ... a un proceso de manejo de datos", pero la diferencia es realmente que, un bloque de datos , no un bloque de datos y el procedimiento (acción, método, comando) ejecutado el entonces. Eso es unaPOST
sobrecarga, y laPOST
sobrecarga es un diseño de estilo RPC, no RESTful.Yo contesté antes , pero esta respuesta contradice mi viejo respuesta y sigue una estrategia muy diferente para llegar a una solución. Muestra cómo se construye la solicitud HTTP a partir de los conceptos que definen REST y HTTP. También usa en
PATCH
lugar dePOST
oPUT
.Pasa por las restricciones REST, luego los componentes de HTTP, luego una posible solución.
DESCANSO
REST es un conjunto de restricciones destinadas a aplicarse a un sistema hipermedia distribuido para hacerlo escalable. Incluso para tener sentido en el contexto del control remoto de una acción, debe pensar en controlar remotamente una acción como parte de un sistema hipermedia distribuido, una parte de un sistema para descubrir, ver y modificar información interconectada. Si eso es más problema de lo que vale, entonces probablemente no sea bueno intentar que sea RESTANTE. Si solo desea una GUI de tipo "panel de control" en el cliente que pueda desencadenar acciones en el servidor a través del puerto 80, entonces probablemente desee una interfaz RPC simple como JSON-RPC a través de solicitudes / respuestas HTTP o un WebSocket.
Pero REST es una forma fascinante de pensar y el ejemplo en la pregunta resulta fácil de modelar con una interfaz RESTful, así que asumamos el desafío por diversión y educación.
REST está definido por cuatro restricciones de interfaz:
Usted pregunta cómo puede definir una interfaz, cumpliendo con estas restricciones, a través de las cuales una computadora le dice a otra computadora que ladre un perro. Específicamente, desea que su interfaz sea HTTP, y no desea descartar las características que hacen que HTTP RESTful se use según lo previsto.
Comencemos con la primera restricción: identificación de recursos .
Entonces un perro es un recurso. Necesita ser identificado.
Puede modelar un perro tomando un conjunto de identificadores y representaciones y diciendo que todos están asociados entre sí en un momento dado. Por ahora, usemos el identificador "perro # 1". Eso nos lleva a la segunda y tercera limitaciones: representación de recursos y autodescripción .
A continuación se muestra una secuencia de bytes que capturan el estado deseado del perro, es decir, la representación que deseamos asociar con el identificador "perro # 1" (tenga en cuenta que solo representa parte del estado, ya que no tiene en cuenta el nombre del perro, la salud , o incluso ladridos pasados):
Se supone que debe adjuntarse a los metadatos que lo describen. Estos metadatos pueden ser útiles:
Finalmente, veamos la cuarta restricción: HATEOAS .
En una interfaz RESTful, el cliente recibe una representación de recursos para averiguar cómo debe recibir o enviar una representación. Debe haber una representación en algún lugar de la aplicación desde la cual el cliente pueda descubrir cómo recibir o enviar todas las representaciones que debería poder recibir o enviar, incluso si sigue una cadena de representaciones para llegar a esa información. Esto parece bastante simple:
El cliente solicita una representación de un recurso identificado como la página de inicio; en respuesta, obtiene una representación que contiene un identificador de cada perro que el cliente pueda desear. El cliente extrae un identificador y le pregunta al servicio cómo puede interactuar con el perro identificado, y el servicio dice que el cliente puede enviar una declaración en inglés que describa parte del estado previsto del perro. Luego, el cliente envía dicha declaración y recibe un mensaje de éxito o un mensaje de error.
HTTP
HTTP implementa restricciones REST de la siguiente manera:
identificación de recursos : URI
representación de recursos : entidad-cuerpo
autodescripción : código de método o estado, encabezados y posiblemente partes del cuerpo de la entidad (por ejemplo, el URI de un esquema XML)
HATEOAS : hipervínculos
Has decidido en
http://api.animals.com/v1/dogs/1
como el URI. Supongamos que el cliente obtuvo esto de alguna página en el sitio.Usemos este cuerpo de entidad (el valor de
next
es una marca de tiempo; un valor de0
significa 'cuando se recibe esta solicitud'):Ahora necesitamos un método. PATCH se ajusta a la descripción de "parte del estado deseado" que decidimos:
Y algunos encabezados:
Para indicar el lenguaje de la entidad-cuerpo:
Content-Type: application/json
Para asegurarse de que solo ocurra una vez:
If-Unmodified-Since: <date/time this was first sent>
Y tenemos una solicitud:
En caso de éxito, el cliente debe recibir un
204
código de estado en respuesta, o205
si la representación de/v1/dogs/1/
ha cambiado para reflejar el nuevo horario de ladridos.En caso de falla, debe recibir un
403
mensaje útil y útil por qué.No es esencial REST para que el servicio refleje el programa de ladridos en una representación en respuesta a
GET /v1/dogs/1/
, pero tendría más sentido si una representación JSON incluyera esto:Trate el trabajo cron como un detalle de implementación que el servidor oculta de la interfaz. Esa es la belleza de una interfaz genérica. El cliente no tiene que saber qué hace el servidor detrás de escena; Lo único que le importa es que el servicio comprenda y responda a los cambios de estado solicitados.
fuente
La mayoría de las personas usan POST para este propósito. Es apropiado para realizar "cualquier operación insegura o no impotente cuando ningún otro método HTTP parece apropiado".
Las API como XMLRPC usan POST para desencadenar acciones que pueden ejecutar código arbitrario. La "acción" se incluye en los datos POST:
El ejemplo de RPC se da para mostrar que POST es la elección convencional de los verbos HTTP para los métodos del lado del servidor. Aquí están los pensamientos de Roy Fielding sobre POST : prácticamente dice que es RESTful usar los métodos HTTP como se especifica.
Tenga en cuenta que RPC en sí no es muy RESTful porque no está orientado a los recursos. Pero si necesita apatridia, almacenamiento en caché o capas, no es difícil realizar las transformaciones adecuadas. Ver http://blog.perfectapi.com/2012/opinionated-rpc-apis-vs-restful-apis/ para ver un ejemplo.
fuente
POST api.animals.com/v1/dogs1?action=bark
/RPC2
no hace nada para identificar un recurso: identifica la tecnología de un servidor. En cambio, esto usamethodName
para tratar de 'identificar' el 'recurso', pero aun así, no se beneficia de la distinción sustantivo / verbo; la única cosa como "verbo" aquí esmethodCall
. Esto es como 'hacer recuperación de nombre de estado' en lugar de 'recuperar nombre de estado': este último tiene mucho más sentido.POST
es el método HTTP diseñado paraLos métodos del lado del servidor que manejan acciones no asignadas a CRUD es lo que Roy Fielding pretendía con REST, por lo que eres bueno allí, y es por eso que
POST
se hizo no idempotente.POST
manejará la mayoría de las publicaciones de datos en métodos del lado del servidor para procesar la información.Dicho esto, en su escenario de ladrido de perros, si desea que se realice un ladrido del lado del servidor cada 10 minutos, pero por alguna razón necesita que el disparador se origine en un cliente,
PUT
serviría mejor el propósito, debido a su idempotencia. Bueno, estrictamente en este escenario no hay riesgo aparente de múltiples solicitudes POST que provoquen que tu perro maulle, pero de todos modos ese es el propósito de los dos métodos similares. Mi respuesta a una pregunta SO similar puede ser útil para usted.fuente
PUT
URL se refiera a lo que debe ser reemplazado por el contenido del cliente y laPOST
URL se refiera a lo que debe procesar el contenido del cliente como quiera.Si asumimos que Barking es un recurso interno / dependiente / secundario sobre el cual el consumidor puede actuar, entonces podríamos decir:
perro número 1 ladra
devuelve la última marca de tiempo de corteza
no aplica! así que ignóralo.
fuente
/v1/dogs/1/bark
que es un recurso per se yPOST
una descripción de cómo debería cambiar el estado interno de ese recurso. Me parece que tiene más sentido considerarlo/v1/dogs/1/
como un recurso e indicar en el cuerpo de la entidad que debería ladrar.Las revisiones anteriores de algunas respuestas sugirieron que usara RPC. No necesita mirar a RPC ya que es perfectamente posible hacer lo que quiera mientras se adhiere a las restricciones REST.
En primer lugar, no coloque parámetros de acción en la URL. La URL define a qué está aplicando la acción, y los parámetros de consulta son parte de la URL. Debe considerarse enteramente como un sustantivo.
http://api.animals.com/v1/dogs/1/?action=bark
es un recurso diferente, un sustantivo diferente parahttp://api.animals.com/v1/dogs/1/
. [nb Asker ha eliminado el?action=bark
URI de la pregunta.] Por ejemplo, compárelohttp://api.animals.com/v1/dogs/?id=1
conhttp://api.animals.com/v1/dogs/?id=2
. Diferentes recursos, que se distinguen solo por la cadena de consulta. Por lo tanto, la acción de su solicitud, a menos que corresponda directamente a un tipo de método existente sin cuerpo (TRACE, OPTIONS, HEAD, GET, DELETE, etc.) debe definirse en el cuerpo de la solicitud.Luego, decida si la acción es " idempotente ", lo que significa que puede repetirse sin efectos adversos (consulte el siguiente párrafo para obtener más explicaciones). Por ejemplo, establecer un valor en verdadero se puede repetir si el cliente no está seguro de que sucedió el efecto deseado. Envían la solicitud nuevamente y el valor sigue siendo verdadero. Agregar 1 a un número no es idempotente. Si el cliente envía el comando Add1, no está seguro de que funcionó y lo envía nuevamente, ¿el servidor agregó uno o dos? Una vez que haya determinado eso, estará en una mejor posición para elegir entre
PUT
yPOST
para su método.Idempotente significa que una solicitud puede repetirse sin cambiar el resultado. Estos efectos no incluyen el registro y otras actividades de administración del servidor. Usando su primer y segundo ejemplo, enviar dos correos electrónicos a la misma persona da como resultado un estado diferente al envío de un correo electrónico (el destinatario tiene dos en su bandeja de entrada, lo que podría considerar spam), por lo que definitivamente usaría POST para eso . Si barkCount en el ejemplo 2 está destinado a ser visto por un usuario de su API o afecta a algo que es visible para el cliente, entonces también es algo que haría que la solicitud no sea idempotente. Si solo debe ser visto por usted, entonces cuenta como un registro del servidor y debe ignorarse al determinar la idempotencia.
Por último, determine si se espera que la acción que desea realizar tenga éxito de inmediato o no. BarkDog es una acción que se completa rápidamente. RunMarathon no lo es. Si su acción es lenta, considere devolver un
202 Accepted
, con una URL en el cuerpo de respuesta para que un usuario realice una encuesta para ver si la acción se ha completado. Alternativamente,/marathons-in-progress/
haga que los usuarios PUBLICEN en una URL de lista como, y luego, cuando la acción esté hecha, rediríjalos de la URL de ID en progreso a/marathons-complete/
URL.Para los casos específicos n. ° 1 y n. ° 2, el servidor alojaría una cola y el cliente le enviaría lotes de direcciones. La acción no sería SendEmails, sino algo así como AddToDispatchQueue. El servidor puede sondear la cola para ver si hay alguna dirección de correo electrónico en espera y enviar correos electrónicos si encuentra alguna. Luego actualiza la cola para indicar que la acción pendiente ahora se ha realizado. Tendría otro URI que muestra al cliente el estado actual de la cola. Para evitar el doble envío de correos electrónicos, el servidor también podría mantener un registro de a quién le envió este correo electrónico y verificar cada dirección para asegurarse de que nunca envíe dos a la misma dirección, incluso si PUBLICA la misma lista dos veces para la cola.
Al elegir un URI para cualquier cosa, trate de pensarlo como un resultado, no como una acción. Por ejemplo,
google.com/search?q=dogs
muestra los resultados de una búsqueda de la palabra "perros". No necesariamente realiza la búsqueda.Los casos # 3 y # 4 de su lista tampoco son acciones idempotentes. Sugiere que los diferentes efectos sugeridos pueden afectar el diseño de la API. En los cuatro casos, usaría la misma API, ya que los cuatro cambian el "estado mundial".
fuente
Vea mi nueva respuesta : contradice esta y explica REST y HTTP de manera más clara y precisa.
Aquí hay una recomendación que resulta RESTANTE, pero ciertamente no es la única opción. Para comenzar a ladrar cuando el servicio recibe la solicitud:
token
es un número arbitrario que evita ladridos redundantes sin importar cuántas veces se envíe esta solicitud.next
indica la hora de la próxima corteza; un valor de0
significa 'ASAP'.Siempre que lo hagas
GET /v1/dogs/1/bark-schedule
, deberías obtener algo como esto, donde t es el tiempo de la última corteza y u es t + 10 minutos:{"last": t, "next": u}
Le recomiendo que use la misma URL para solicitar un ladrido que usa para averiguar sobre el estado actual de ladrido del perro. No es esencial para REST, pero enfatiza el acto de modificar el horario.
El código de estado apropiado es probablemente 205 . Me estoy imaginando a un cliente que mira el horario actual,
POST
a la misma URL para cambiarlo, y el servicio le indica que le dé un segundo vistazo al horario para probar que se ha cambiado.Explicación
DESCANSO
Olvídate de HTTP por un momento. Es esencial comprender que un recurso es una función que toma tiempo como entrada y devuelve un conjunto que contiene identificadores y representaciones . Simplifiquemos eso para: un recurso es un conjunto R de identificadores y representaciones; R puede cambiar: los miembros se pueden agregar, eliminar o modificar. (A pesar de que es malo, diseño inestable a eliminar o modificar los identificadores.) Decimos un identificador que es un elemento de R identifica R , y que una representación que es un elemento de R representa R .
Digamos que R es un perro. Por casualidad identificas a R como
/v1/dogs/1
. (Es decir,/v1/dogs/1
es un miembro del R .) Eso es sólo una de las muchas maneras en que podría identificar R . También puede identificar R como/v1/dogs/1/x-rays
y como/v1/rufus
.¿Cómo representas a R ? Quizás con una fotografía. Tal vez con un conjunto de radiografías. O tal vez con una indicación de la fecha y hora en que R ladró por última vez. Pero recuerde que todas estas son representaciones del mismo recurso .
/v1/dogs/1/x-rays
es un identificador del mismo recurso que está representado por una respuesta a la pregunta "¿cuándo ladró R por última vez?"HTTP
Las representaciones múltiples de un recurso no son muy útiles si no puede referirse al que desea. Es por eso que HTTP es útil: le permite conectar identificadores a representaciones . Es decir, es una forma de que el servicio reciba una URL y decida qué representación atender al cliente.
Al menos, eso es lo que
GET
hace.PUT
es básicamente el inverso deGET
:PUT
una representación r en la URL si desea que futurasGET
solicitudes a esa URL devuelvan r , con algunas posibles traducciones como JSON a HTML.POST
es una forma más flexible de modificar una representación. Piense en que hay lógica de visualización y lógica de modificación que son equivalentes entre sí, ambas correspondientes a la misma URL. Una solicitud POST es una solicitud para que la lógica de modificación procese la información y modifique cualquier representación (no solo la representación ubicada en la misma URL) que el servicio considere conveniente. Presta atención al tercer párrafo después de 9.6 PUT : no estás reemplazando la cosa en la URL con contenido nuevo; le está pidiendo a la cosa en la URL que procese cierta información y responda inteligentemente en forma de representaciones informativas.En nuestro caso, le pedimos a la lógica de modificación en
/v1/dogs/1/bark-schedule
(que es la contraparte de la lógica de visualización que nos dice cuándo ladró por última vez y cuándo ladrará a continuación) que procese nuestra información y modifique algunas representaciones en consecuencia. En respuesta a futurosGET
s, la lógica de visualización correspondiente a la misma URL nos dirá que el perro ahora está ladrando como deseamos.Piense en el trabajo cron como un detalle de implementación. Ofertas HTTP en visualización y modificación de representaciones. A partir de ahora, el servicio le dirá al cliente cuándo ladró el perro por última vez y cuándo ladrará a continuación. Desde la perspectiva del servicio, eso es honesto porque esos tiempos corresponden con trabajos cron pasados y planificados.
fuente
REST es un estándar orientado a los recursos, no está impulsado por la acción como sería un RPC.
Si desea que su servidor ladre , debe buscar diferentes ideas como JSON-RPC o la comunicación websockets.
En mi opinión, cada intento de mantenerlo RESTful fallará: puede emitir un
POST
con elaction
parámetro, no está creando ningún recurso nuevo, pero como puede tener efectos secundarios, está más seguro.fuente
POST
fue diseñado para "proporcionar un bloque de datos ... a un proceso de manejo de datos" . Parece que mucha gente distingue los recursos de las acciones, pero en realidad las acciones son solo un tipo de recurso. Llamar a un recurso de acción en un servidor sigue siendo una interfaz uniforme, almacenable en caché, modular y escalable. También es apátrida, pero eso se puede violar si el cliente está diseñado para esperar una respuesta. Pero llamar a un "método vacío" en el servidor es lo que Roy Fielding pretendía con REST .