verbos ventajas una tutorial sirve servicios qué para español ejemplo diseño api rest url api-design restful-architecture

una - ventajas api rest



Llamar a un método del lado del servidor en un recurso de una manera RESTful (8)

¿Por qué apuntar a un diseño RESTful?

Los principios RESTful traen las características que hacen que los sitios web sean más fáciles (para que un usuario humano aleatorio los "navegue") al diseño de API de servicios web , por lo que son fáciles de usar para un programador. RESTO 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 servicios POST sobrecargados URI únicos), lo que algunos pueden llamar "falta de características" , es en realidad su mayor fortaleza . De inmediato, HTTP le pide que tenga capacidad de direccionamiento y apatridia : las dos decisiones de diseño básicas que mantienen HTTP escalable hasta mega-sitios de hoy (y megaservicios).

Pero REST no es el tope de plata: a veces un estilo RPC (llamada de procedimiento remoto, como SOAP) puede ser apropiado y, a veces, 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 trae servicios de estilo RPC para un trabajo que el antiguo HTTP simple podría manejar muy bien. El efecto es que HTTP se reduce a un protocolo de transporte para una enorme carga XML que explica qué está pasando "realmente" (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 forma que un código Java / C # no puede estar orientado a objetos, solo el uso de HTTP no hace un diseño RESTful. Uno puede verse atrapado en la prisa de pensar acerca de sus servicios en términos de acciones y métodos remotos que deberían llamarse. No es de extrañar que esto termine en un servicio RPC-Style (o un REST-RPC-hybrid). El primer paso es pensar diferente. Un diseño REST se puede lograr de muchas maneras, una forma (la más simple, podría decirse) es pensar en su aplicación en términos de recursos, no de acciones:

  • En lugar de pensar en términos de acciones ("hacer una búsqueda de lugares en el mapa"),
  • Piense en términos de los resultados de esa acción ("la lista de lugares en el mapa que coinciden con los criterios de búsqueda").

Iré por ejemplos a continuación. (Otro aspecto clave de REST es el uso de HATEOAS; no lo cepillé aquí, pero hablo de ello rápidamente en otra publicación ).

Sobre el primer diseño

Echemos un vistazo al diseño propuesto:

ACTION http://api.animals.com/v1/dogs/1/

En primer lugar, no deberíamos considerar la creación de un nuevo verbo HTTP ( ACTION ). En términos generales, esto es indeseable por varias razones:

  • (1) Dado solo el URI de servicio, ¿cómo sabrá un programador "aleatorio" que existe el verbo ACTION ?
  • (2) si el programador sabe que existe, ¿cómo sabrá su semántica? ¿Qué significa ese verbo?
  • (3) ¿qué propiedades (seguridad, idempotencia) uno esperaría que tuviera ese verbo?
  • (4) ¿Qué pasa si el programador tiene un cliente muy simple que solo maneja verbos HTTP estándar?
  • (5) ...

Ahora consideremos el uso de POST (discutiré por qué a continuación, solo tome mi palabra para eso ahora):

POST /v1/dogs/1/ HTTP/1.1 Host: api.animals.com {"action":"bark"}

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 la fábrica). Un "procesador de documentos" es un URI que simplemente "lanzará cosas" y "olvidará" 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 ya apostaría que ambos no son ciertos:

  • {"action":"bark"} no es un documento , en realidad es el método que estás tratando de infiltrarse ninja en el servicio; y
  • el /v1/dogs/1/ URI representa un recurso "perro" (probablemente el perro con id==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 puedes inferir nada de eso. ¿Cómo sabría un programador que un perro tiene una acción de bark que puede ser infundida en secreto con un POST en ella?

Diseñando las llamadas API de su pregunta

Así que vayamos a la caza e intentemos diseñar esos ladridos DESCANSO pensando en términos de recursos . Permítame citar el libro de Restful Web Services :

Una solicitud POST es un intento de crear un nuevo recurso a partir de uno existente. El recurso existente puede ser el padre del nuevo en un sentido de estructura de datos, la forma en que la raíz de un árbol es el padre de todos sus nodos hoja. O el recurso existente puede ser un recurso especial de "fábrica" cuyo único propósito es generar otros recursos. La representación enviada junto con una solicitud POST describe el estado inicial del nuevo recurso. Al igual que con PUT, una solicitud POST no necesita incluir una representación en absoluto.

Siguiendo la descripción anterior, podemos ver que la bark se puede modelar como un subrecurso de un dog (ya que una bark está contenida dentro de un perro, es decir, un perro ladra).

De ese razonamiento que ya tenemos:

  • El método es POST
  • El recurso es /barks , el subrecurso de perro: /v1/dogs/1/barks , que representa una bark "fábrica". Ese URI es único para cada perro (ya que está en /v1/dogs/{id} ).

Ahora cada caso de su lista tiene un comportamiento específico.

1. ladrar simplemente envía un correo electrónico a dog.email y no registra nada.

En primer lugar, ¿está ladrando (enviando un correo electrónico) una tarea síncrona o asíncrona? En segundo lugar, ¿la solicitud de bark requiere algún documento (el correo electrónico, tal vez) o está vacío?


1.1 ladrido envía un correo electrónico a dog.email y no registra nada (como una tarea síncrona)

Este caso es simple. Una llamada al recurso de la fábrica barks produce un ladrido (un correo electrónico enviado) de inmediato y la respuesta (si está OK o no) se da de inmediato:

POST /v1/dogs/1/barks HTTP/1.1 Host: api.animals.com Authorization: Basic mAUhhuE08u724bh249a2xaP= (entity-body is empty - or, if you require a **document**, place it here) 200 OK

Como registra (cambia) nada, 200 OK es suficiente. Muestra que todo salió como se esperaba.


1.2 ladrido envía un correo electrónico a dog.email y no registra nada (como una tarea asincrónica)

En este caso, el cliente debe tener una forma de rastrear la tarea de bark . La tarea de bark debe ser un recurso con su propia URI .:

POST /v1/dogs/1/barks HTTP/1.1 Host: api.animals.com Authorization: Basic mAUhhuE08u724bh249a2xaP= {document body, if needed; NOTE: when possible, the response SHOULD contain a short hypertext note with a hyperlink to the newly created resource (bark) URI, the same returned in the Location header (also notice that, for the 202 status code, the Location header meaning is not standardized, thus the importance of a hipertext/hyperlink response)} 202 Accepted Location: http://api.animals.com/v1/dogs/1/barks/a65h44

De esta forma, cada bark es rastreable. El cliente puede emitir un GET al URI de bark para saber su estado actual. Tal vez incluso use un DELETE para cancelarlo.


2. ladrar envía un correo electrónico a dog.email y luego incrementa dog.barkCount por 1

Este puede ser más complicado, si desea que el cliente sepa que el recurso del dog se ha cambiado:

POST /v1/dogs/1/barks HTTP/1.1 Host: api.animals.com Authorization: Basic mAUhhuE08u724bh249a2xaP= {document body, if needed; when possible, containing a hipertext/hyperlink with the address in the Location header -- says the standard} 303 See Other Location: http://api.animals.com/v1/dogs/1

En este caso, la intención del encabezado de la location es hacerle saber al cliente que debe echarle un vistazo al dog . Desde el HTTP RFC aproximadamente 303 :

Este método existe principalmente para permitir el resultado de una secuencia de comandos activada POST para redirigir el agente de usuario a un recurso seleccionado.

Si la tarea es asincrónica, se necesita un subcampo de bark al igual que la situación 1.2 y el 303 debe devolver en un GET .../barks/Y cuando la tarea se completa.


3. ladrido crea un nuevo registro " bark " con la grabación bark.timestamp cuando se produjo la corteza. También incrementa dog.barkCount en 1.

POST /v1/dogs/1/barks HTTP/1.1 Host: api.animals.com Authorization: Basic mAUhhuE08u724bh249a2xaP= (document body, if needed) 201 Created Location: http://api.animals.com/v1/dogs/1/barks/a65h44

Aquí, la bark se creó debido a la solicitud, por lo que se aplica el estado 201 Created .

Si la creación es asincrónica, se requiere un 202 Accepted ( como dice el RFC HTTP ) en su lugar.

La marca de tiempo guardada es una parte del recurso de bark y se puede recuperar con un GET . El perro actualizado puede ser "documentado" en que GET dogs/X/barks/Y también.


4. ladrido 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 indicándoles que el nuevo código de perro está en producción.

La redacción de este es complicada, pero prácticamente es una simple tarea asincrónica:

POST /v1/dogs/1/barks HTTP/1.1 Host: api.animals.com Authorization: Basic mAUhhuE08u724bh249a2xaP= (document body, if needed) 202 Accepted Location: http://api.animals.com/v1/dogs/1/barks/a65h44

El cliente luego emitiría GET a /v1/dogs/1/barks/a65h44 para conocer el estado actual (si el código se extrajo, si el correo electrónico se envió al propietario y tal). Cada vez que el perro cambia, un 303 es aplicable.


Terminando

Citando a Roy Fielding :

Lo único que REST requiere de los métodos es que se definan de manera uniforme para todos los recursos (es decir, para que los intermediarios no tengan que conocer el tipo de recurso para comprender el significado de la solicitud).

En los ejemplos anteriores, POST está diseñado de manera uniforme. Hará que el perro " bark ". Eso no es seguro (lo que significa que la corteza tiene efectos sobre los recursos), ni idempotente (cada solicitud arroja una nueva bark ), lo que encaja bien con el verbo POST .

Un programador sabría: un POST para bark produce un bark . Los códigos de estado de la respuesta (también con entidad-cuerpo y encabezados cuando sea necesario) hacen el trabajo de explicar qué cambió y cómo el cliente puede y debe continuar.

Nota: Las principales fuentes 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 que se crearon por primera vez. La pregunta original sobre el diseño de un URI como:

ACTION http://api.animals.com/v1/dogs/1/?action=bark

A continuación se encuentra 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 .

  • Los servicios web RESTful transmiten información sobre el método en el método HTTP.
  • Los servicios típicos RPC-Style y SOAP mantienen los suyos en el cuerpo de la entidad y en el encabezado HTTP.

QUÉ PARTE de los datos [en los que el cliente quiere que funcione el servidor] es la información del alcance .

  • Los servicios RESTful usan el URI. Los servicios SOAP / RPC-Style una vez más usan el cuerpo de la entidad y los encabezados HTTP.

Como ejemplo, tome el URI de Google http://www.google.com/search?q=DOG . Allí, la información del método es GET y la información del alcance es /search?q=DOG .

Larga historia corta:

  • En arquitecturas RESTful , la información del método va al método HTTP.
  • En Arquitecturas orientadas a recursos , la información de alcance entra en el URI.

Y la regla de oro:

Si el método HTTP no coincide con la información del método, el servicio no es RESTful. Si la información del alcance no está en el URI, el servicio no está orientado a recursos.

Puede poner la " acción" de "ladrido" en la URL (o en el cuerpo de la entidad) y usar POST . No hay problema allí, funciona, y puede ser la forma más sencilla de hacerlo, pero esto no es RESTful .

Para mantener su servicio realmente RESTful, es posible que tenga que dar un paso atrás y pensar en lo que realmente quiere hacer aquí (qué efectos tendrá en los recursos).

No puedo hablar sobre las necesidades específicas de su negocio, pero permítame darle un ejemplo: considere un servicio RESTful ordering donde los pedidos se encuentran en URI como example.com/order/123 .

Ahora di que queremos cancelar un pedido, ¿cómo lo haremos? Uno puede sentirse tentado de pensar que es una "acción" de "cancelación " y diseñarla como POST example.com/order/123?do=cancel .

Eso no es RESTful, como hablamos anteriormente. En su lugar, podríamos PUT una nueva representación del order con un elemento canceled enviado a true :

PUT /order/123 HTTP/1.1 Content-Type: application/xml <order id="123"> <customer id="89987">...</customer> <canceled>true</canceled> ... </order>

Y eso es. Si la orden no se puede cancelar, se puede devolver un código de estado específico. (Un diseño de sub-fuente, como POST /order/123/canceled con la entidad-cuerpo true puede, por simplicidad, también estar disponible).

En su escenario específico, puede intentar algo similar. De esta forma, mientras un perro ladra, por ejemplo, un GET en /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 quédese con POST .

Actualizar:

No quiero que la respuesta sea demasiado grande, pero lleva un tiempo entender el 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 coinciden con los criterios de búsqueda" ).

Puede encontrar que vuelve 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 recursos nuevos ( /post?lang=en es claramente el mismo recurso que /post?lang=jp , solo una representación diferente). Por el contrario, se utilizan para transmitir el estado del cliente (como ?page=10 , por lo que el estado no se guarda 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 ). Una vez más, no recursos distintos.

Propiedades de los verbos HTTP (métodos):

Otro punto claro que muestra ?action=something en el URI no es RESTful, son las propiedades de los verbos HTTP:

  • GET y HEAD son seguros (e idempotentes);
  • PUT y DELETE son idempotentes solamente;
  • POST no es ninguno.

Seguridad : una solicitud GET o HEAD es una solicitud para leer algunos datos, no una solicitud para cambiar el estado de ningún servidor. El cliente puede hacer una solicitud GET o HEAD 10 veces y es lo mismo que hacerla una sola vez o nunca hacerlo .

Idempotencia : una operación idempotente en una que tiene el mismo efecto ya sea que la aplique una o más veces (en matemáticas, multiplicar por cero es idempotente). Si DELETE un recurso una vez, eliminarlo tendrá el mismo efecto (el recurso ya se ha GONE ).

POST no es seguro ni idempotente. Hacer dos solicitudes POST idénticas a un recurso ''de fábrica'' probablemente resultará en dos recursos subordinados que contengan la misma información. Con POST sobrecargado (método en URI o entidad-cuerpo), todas las apuestas están desactivadas.

Ambas propiedades son importantes para el éxito del protocolo HTTP (¡sobre redes no 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 rompe claramente el contrato de los métodos HTTP. Una vez más, la tecnología te permite, puedes hacerlo, pero eso no es un diseño RESTful.

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 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? Yo 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 crear mi propio método HTTP, ayúdame y dame una mejor idea sobre cómo debo invocar un método del lado del servidor de una manera RESTANTE. :)

EDITAR PARA ACLARAR

Algunas aclaraciones más sobre lo que hace el método de "ladrido". Estas son algunas opciones que pueden dar como resultado llamadas API estructuradas de manera diferente:

  1. ladra solo envía un correo electrónico a dog.email y no registra nada.
  2. ladrar envía un correo electrónico a dog.email y los incrementos de dog.barkCount por 1.
  3. ladrar crea un nuevo registro de "ladrido" con la grabación bark.timestamp cuando se produjo la corteza. También incrementa dog.barkCount en 1.
  4. ladra 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 indicándoles que el nuevo código de perro está en producción.

La mayoría de las personas usa POST para este propósito. Es apropiado para realizar "cualquier operación insegura o no volátil cuando ningún otro método HTTP parece apropiado".

APIs como XMLRPC usan POST para desencadenar acciones que pueden ejecutar código arbitrario. La "acción" está incluida en los datos POST:

POST /RPC2 HTTP/1.0 User-Agent: Frontier/5.1.2 (WinNT) Host: betty.userland.com Content-Type: text/xml Content-length: 181 <?xml version="1.0"?> <methodCall> <methodName>examples.getStateName</methodName> <params> <param> <value><i4>41</i4></value> </param> </params> </methodCall>

El RPC es un ejemplo para mostrar que POST es la elección convencional de verbos HTTP para los métodos del lado del servidor. Aquí están los pensamientos de Roy Fielding sobre el POST : prácticamente dice que es RESTful utilizar los métodos HTTP como se especifica.

Tenga en cuenta que RPC en sí no es muy RESTful porque no está orientado a 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.


Si asumimos que Barking es un recurso interno / dependiente / secundario en el que el consumidor puede actuar, entonces podríamos decir:

POST http://api.animals.com/v1/dogs/1/bark

perro número 1 ladra

GET http://api.animals.com/v1/dogs/1/bark

devuelve la última marca de tiempo de corteza

DELETE http://api.animals.com/v1/dogs/1/bark

no aplica! así que ignóralo.


POST es el método HTTP diseñado para

Proporcionar un bloque de datos ... a un proceso de manejo de datos

Los métodos del lado del servidor que manejan acciones no mapeadas por CRUD es lo que Roy Fielding quiso con REST, por lo que está bien allí, y es por eso que POST se hizo para ser no idempotente. POST manejará la mayoría de las publicaciones de datos a los 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 desencadenante se origine de un cliente, PUT podría servir mejor al propósito, debido a su idempotencia. Bueno, estrictamente por este escenario no hay riesgo aparente de múltiples solicitudes POST que causen a su perro maullar en su lugar, 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.


Respondí antes , pero esta respuesta contradice mi respuesta anterior y sigue una estrategia muy diferente para llegar a una solución. Muestra cómo se crea la solicitud HTTP a partir de los conceptos que definen REST y HTTP. También usa PATCH lugar de POST o PUT .

Pasa por las restricciones REST, luego por los componentes de HTTP, luego una posible solución.

DESCANSO

REST es un conjunto de restricciones destinadas a ser aplicadas a un sistema hipermedia distribuido para hacerlo escalable. Incluso para darle 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 problemas de lo que vale, entonces probablemente no sea bueno tratar de hacerlo RESTful. Si solo desea una GUI 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 es fácil de modelar con una interfaz RESTful, así que asumamos el desafío por diversión y educación.

REST se defined mediante cuatro restricciones de interfaz:

identificación de recursos; manipulación de recursos a través de representaciones; mensajes autodescriptivos; e hipermedia como el motor del estado de la aplicación.

Usted pregunta cómo puede definir una interfaz que cumpla con estas restricciones, a través de la cual 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 como estaba previsto.

Comencemos con la primera restricción: identificación de recursos .

Cualquier información que pueda nombrarse puede ser un recurso: un documento o imagen, un servicio temporal (por ejemplo, "el clima actual en Los Ángeles"), una colección de otros recursos, un objeto no virtual (por ejemplo, una persona), etc. .

Entonces un perro es un recurso. Necesita ser identificado.

Más precisamente, un recurso R es una función de pertenencia que varía temporalmente M R ( t ), que para el tiempo t mapea a un conjunto de entidades, o valores, que son equivalentes. Los valores en el conjunto pueden ser representaciones de recursos y / o identificadores de recursos .

Modelas 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 n. ° 1". Eso nos lleva a la segunda y tercera restricciones: representación de recursos y autodescripción .

Los componentes REST realizan acciones en un recurso mediante el uso de una representación para capturar el estado actual o previsto de ese recurso y la transferencia de esa representación entre los componentes. Una representación es una secuencia de bytes, más metadatos de representación para describir esos bytes.

A continuación se muestra una secuencia de bytes que captura el estado deseado del perro, es decir, la representación que deseamos asociar con el identificador "perro n. ° 1" (tenga en cuenta que solo representa parte del estado ya que no considera el nombre del perro, salud , o incluso ladridos del pasado):

Ha estado ladrando cada 10 minutos desde el momento en que se realizó este cambio de estado, y continuará indefinidamente.

Se supone que debe estar adjunto a los metadatos que lo describen. Estos metadatos pueden ser útiles:

Es una declaración en inglés. Describe parte del estado deseado. Si se recibe varias veces, solo permita que el primero tenga un efecto.

Finalmente, veamos la cuarta restricción: HATEOAS .

REST ... ve una aplicación como una estructura coherente de información y alternativas de control a través de las cuales un usuario puede realizar una tarea deseada. Por ejemplo, buscar una palabra en un diccionario en línea es una aplicación, como hacer un recorrido por un museo virtual o revisar un conjunto de notas de clase para estudiar para un examen. ... El siguiente estado de control de una aplicación reside en la representación del primer recurso solicitado, por lo que obtener esa primera representación es una prioridad. ... La aplicación de modelo es, por lo tanto, un motor que se mueve de un estado al siguiente al examinar y elegir entre las transiciones de estado alternativas en el conjunto actual de representaciones.

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 averiguar cómo recibir o enviar todas las representaciones que debería 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 podría querer. El cliente extrae un identificador del mismo 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 intencionado del perro. Luego el cliente envía tal declaración y recibe un mensaje de éxito o un mensaje de error.

HTTP

HTTP implementa las restricciones REST de la siguiente manera:

identificación de recursos : URI

representación de recursos : entidad-cuerpo

autodescripción : método o código de estado, encabezados y posiblemente partes del cuerpo de la entidad (por ejemplo, el URI de un esquema XML)

HATEOAS : hipervínculos

Has decidido 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 de 0 significa ''cuando se recibe esta solicitud''):

{"barks": {"next": 0, "frequency": 10}}

Ahora necesitamos un método. PATCH ajusta a la descripción de "parte del estado deseado" que decidimos:

El método PATCH solicita que un conjunto de cambios descritos en la entidad de solicitud se apliquen al recurso identificado por el URI de solicitud.

Y algunos encabezados:

Para indicar el idioma del cuerpo de la entidad: Content-Type: application/json

Para asegurarse de que solo suceda una vez: If-Unmodified-Since: <date/time this was first sent>

Y tenemos una solicitud:

PATCH /v1/dogs/1/ HTTP/1.1 Host: api.animals.com Content-Type: application/json If-Unmodified-Since: <date/time this was first sent> [other headers] {"barks": {"next": 0, "frequency": 10}}

En caso de éxito, el cliente debe recibir un código de estado de 204 en respuesta, o un 205 si la representación de /v1/dogs/1/ ha cambiado para reflejar el nuevo horario de ladrido.

En caso de falla, debe recibir un 403 y un mensaje útil por qué.

No es esencial REST para que el servicio refleje el cronograma de corte en una representación en respuesta a GET /v1/dogs/1/ , pero tendría más sentido si una representación JSON incluyera esto:

"barks": { "previous": [x_1, x_2, ..., x_n], "next": x_n, "frequency": 10 }

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.


See my new answer -- it contradicts this one and explains REST and HTTP more clearly and accurately.

Here''s a recommendation that happens to be RESTful but is certainly not the only option. To start barking when the service receives the request:

POST /v1/dogs/1/bark-schedule HTTP/1.1 ... {"token": 12345, "next": 0, "frequency": 10}

token is an arbitrary number that prevents redundant barks no matter how many times this request is sent.

next indicates the time of the next bark; a value of 0 means ''ASAP''.

Whenever you GET /v1/dogs/1/bark-schedule , you should get something like this, where t is the time of the last bark and u is t + 10 minutes:

{"last": t, "next": u}

I highly recommend that you use the same URL to request a bark that you use to find out about the dog''s current barking state. It''s not essential to REST, but it emphasizes the act of modifying the schedule.

The appropriate status code is probably 205 . I''m imagining a client that looks at the current schedule, POST s to the same URL to change it, and is instructed by the service to give the schedule a second look to prove that it has been changed.

Explicación

DESCANSO

Forget about HTTP for a moment. It''s essential to understand that a resource is a function that takes time as input and returns a set containing identifiers and representations . Let''s simplify that to: a resource is a set R of identifiers and representations; R can change -- members can be added, removed, or modified. (Though it''s bad, unstable design to remove or modify identifiers.) We say an identifier that is an element of R identifies R , and that a representation that is an element of R represents R .

Let''s say R is a dog. You happen to identify R as /v1/dogs/1 . (Meaning /v1/dogs/1 is a member of R .) That''s just one of many ways you could identify R . You could also identify R as /v1/dogs/1/x-rays and as /v1/rufus .

How do you represent R ? Maybe with a photograph. Maybe with a set of X-rays. Or maybe with an indication of the date and time when R last barked. But remember that these are all representations of the same resource . /v1/dogs/1/x-rays is an identifier of the same resource that is represented by an answer to the question "when did R last bark?"

HTTP

Multiple representations of a resource aren''t very useful if you can''t refer to the one you want. That''s why HTTP is useful: it lets you connect identifiers to representations . That is, it is a way for the service to receive a URL and decide which representation to serve to the client.

At least, that''s what GET does. PUT is basically the inverse of GET : you PUT a representation r at the URL if you wish for future GET requests to that URL to return r , with some possible translations like JSON to HTML.

POST is a looser way of modifying a representation. Think of there being display logic and modification logic that are counterparts to each other -- both corresponding to the same URL. A POST request is a request for the modification logic to process the information and modify any representations (not just the representation located by the same URL) as the service sees fit. Pay attention to the third paragraph after 9.6 PUT : you''re not replacing the thing at the URL with new content; you''re asking the thing at the URL to process some information and intelligently respond in the form of informative representations.

In our case, we ask the modification logic at /v1/dogs/1/bark-schedule (which is the counterpart to the display logic that tells us when it last barked and when it will next bark) to process our information and modify some representations accordingly. In response to future GET s, the display logic corresponding to the same URL will tell us that the dog is now barking as we wish.

Think of the cron job as an implementation detail. HTTP deals in viewing and modifying representations. From now on, the service will tell the client when the dog last barked and when it will bark next. From the service''s perspective, that is honest because those times correspond with past and planned cron jobs.


Earlier revisions of some answers suggested you use RPC. You do not need to look to RPC as it is perfectly possible to do what you want whilst adhering to the REST constraints.

Firstly, don''t put action parameters in the URL. The URL defines what you are applying the action to, and query parameters are part of the URL. It should be thought of entirely as a noun. http://api.animals.com/v1/dogs/1/?action=bark is a different resource — a different noun — to http://api.animals.com/v1/dogs/1/ . [nb Asker has removed the ?action=bark URI from the question.] For example, compare http://api.animals.com/v1/dogs/?id=1 to http://api.animals.com/v1/dogs/?id=2 . Different resources, distinguished only by query string. So the action of your request, unless it corresponds directly to a bodyless existing method type (TRACE, OPTIONS, HEAD, GET, DELETE, etc) must be defined in the request body.

Next, decide if the action is " idempotent ", meaning that it can be repeated without adverse effect (see next paragraph for more explanaton). For example, setting a value to true can be repeated if the client is unsure that the desired effect happened. They send the request again and the value remains true. Adding 1 to a number is not idempotent. If the client sends the Add1 command, isn''t sure it worked, and sends it again, did the server add one or two? Once you have determined that, you''re in a better position to choose between PUT and POST for your method.

Idempotent means a request can be repeated without changing the outcome. These effects do not include logging and other such server admin activity. Using your first and second examples, sending two emails to the same person does result in a different state than sending one email (the recipient has two in their inbox, which they might consider to be spam), so I would definitely use POST for that. If the barkCount in example 2 is intended to be seen by a user of your API or affects something that is client-visible, then it is also something that would make the request non-idempotent. If it is only to be viewed by you then it counts as server logging and should be ignored when determing idempotentcy.

Lastly, determine if the action you want to perform can be expected to succeed immediately or not. BarkDog is a quickly completing action. RunMarathon is not. If your action is slow, consider returning a 202 Accepted , with a URL in the response body for a user to poll to see if the action is complete. Alternatively, have users POST to a list URL like /marathons-in-progress/ and then when the action is done, redirect them from the in progress ID URL to the /marathons-complete/ URL.
For the specific cases #1 and #2, I would have the server host a queue, and the client post batches of addresses to it. The action would not be SendEmails, but something like AddToDispatchQueue. The server can then poll the queue to see if there are any email addresses waiting, and send emails if it finds any. It then updates the queue to indicate that the pending action has now been performed. You would have another URI showing the client the current state of the queue. To avoid double-sending of emails, the server could also keep a log of who it has sent this email to, and check each address against that to ensure it never sends two to the same address, even if you POST the same list twice to the queue.

When choosing a URI for anything, try to think of it as a result, not an action. For example google.com/search?q=dogs shows the results of a search for the word "dogs". It does not necessarilly perform the search.

Cases #3 and #4 from your list are also not idempotent actions. You suggest that the different suggested effects might affect the API design. In all four cases I would use the same API, as all four change the “world state.”


REST is a resource oriented standard, it is not action driven as a RPC would be.

If you want your server to bark , you should look into different ideas like JSON-RPC , or into websockets communication.

Every try to keep it RESTful will fail in my opinion: you can issue a POST with the action parameter, you are not creating any new resources but as you may have side effects, you are safer.