API RESTful: ¿Verbos HTTP con URL compartidas o específicas?

25

Al crear una API RESTful , ¿debería usar verbos HTTP en la misma URL (cuando sea posible) o debería crear una URL específica por acción?

Por ejemplo:

GET     /items      # Read all items
GET     /items/:id  # Read one item
POST    /items      # Create a new item
PUT     /items/:id  # Update one item
DELETE  /items/:id  # Delete one item

O con URL específicas como:

GET     /items            # Read all items
GET     /item/:id         # Read one item
POST    /items/new        # Create a new item
PUT     /item/edit/:id    # Update one item
DELETE  /item/delete/:id  # Delete one item
53777A
fuente

Respuestas:

46

En su último esquema, mantiene los verbos en las URL de sus recursos. Esto debe evitarse ya que los verbos HTTP deben usarse para ese propósito. Adopte el protocolo subyacente en lugar de ignorarlo, duplicarlo o anularlo.

Solo mire DELETE /item/delete/:id, coloca la misma información dos veces en la misma solicitud. Esto es superfluo y debe evitarse. Personalmente, estaría confundido con esto. ¿La API realmente admite DELETEsolicitudes? ¿Qué sucede si coloco deleteen la URL y uso un verbo HTTP diferente? ¿Coincidirá con algo? Si es así, ¿cuál será elegido? Como cliente de una API correctamente diseñada, no debería tener que hacer esas preguntas.

Tal vez lo necesite de alguna manera para admitir clientes que no pueden emitir DELETEo PUTsolicitar. Si ese es el caso, pasaría esta información en un encabezado HTTP. Algunas API usan un X-HTTP-Method-Overrideencabezado para este propósito específico (que creo que es bastante feo de todos modos). Sin embargo, ciertamente no colocaría los verbos en los caminos.

Ir por

GET     /items      # Read all items
GET     /items/:id  # Read one item
POST    /items      # Create a new item
PUT     /items/:id  # Update one item
DELETE  /items/:id  # Delete one item

Lo importante de los verbos es que ya están bien definidos en la especificación HTTP y mantenerse en línea con estas reglas le permite usar cachés, proxies y posiblemente otras herramientas externas a su aplicación que entienden la semántica de HTTP pero no la semántica de su aplicación . Tenga en cuenta que la razón por la que debe evitar tenerlos en sus URL no se debe a que las API RESTful requieren URL legibles. Se trata de evitar ambigüedades innecesarias.

Además, una API RESTful puede asignar estos verbos (o cualquier subconjunto de los mismos) a cualquier conjunto de semántica de aplicaciones, siempre que no vaya en contra de la especificación HTTP. Por ejemplo, es perfectamente posible construir una API REST que sólo utiliza las peticiones GET si todas las operaciones que permite son tanto segura y idempotente . La asignación anterior es solo un ejemplo que se ajusta a su caso de uso y cumple con las especificaciones. No necesariamente tiene que ser así.

Tenga en cuenta también que una API verdaderamente RESTful nunca debería requerir que un programador lea una extensa documentación de las URL disponibles siempre que cumpla con el principio HATEOAS (Hipertexto como motor del estado de la aplicación), que es uno de los supuestos básicos de REST . Los enlaces pueden ser completamente incomprensibles para los humanos siempre que la aplicación cliente pueda comprenderlos y utilizarlos para descubrir posibles transiciones de estado de la aplicación.

toniedzwiedz
fuente
44
En ausencia de PUTy DELETE, preferiría agregarlo a la ruta, no diferenciarlo con una cadena de consulta. No es una modificación de cadena de consulta a una operación existente; Es una operación separada.
Robert Harvey
44
@RobertHarvey en este caso, lo llamaría un truco de todos modos. Como usted dice, es una operación y eso no es algo que pondría en el camino al diseñar una API que pretende ser RESTful. Colocarlo en la cadena de consulta parece menos invasivo. Previene el almacenamiento en caché, pero no creo que las respuestas a este tipo de solicitudes se deban en caché de todos modos. También permite al consumidor de la API indicar fácilmente el método sin analizar o construir la URL. Idealmente, una API verdaderamente RESTful debería proporcionar los hipervínculos sin requerir que los clientes creen ellos mismos las URL.
toniedzwiedz
Si no tienes todos los verbos, de todos modos no es completamente RESTful, ¿verdad?
Robert Harvey
@RobertHarvey es cierto, pero los trato como una alternativa, no como el diseño previsto. Me imagino que la API debería admitir métodos HTTP reales y si algún cliente no puede implementarlos por algún motivo, simplemente puede reemplazar su uso con estos parámetros de consulta. Un proxy incluso podría tomarlos sobre la marcha y transformar las solicitudes en unas utilizando verbos HTTP genuinos para que el servidor ni siquiera tenga que preocuparse. Pocas API son realmente RESTful. Cuando se trata de API web genéricas, es realmente una cuestión de gustos. Personalmente, iría por URL limpias. Más fácil de entender en mi humilde opinión.
toniedzwiedz
1
@RobertHarvey como se explicó, no es la forma prevista de usarlos. Me parece que este es el menor de dos males para cuando tienes que superar las limitaciones del cliente. Recuerdo haber leído una documentación para tal API, pero tendré que hacer una excavación en el historial / marcadores de mi navegador para encontrarla. Ahora que lo pienso, un encabezado podría ser mejor en este caso. ¿Estarías de acuerdo?
toniedzwiedz
14

El primero.

Un URI / URL es un identificador de recursos (pista en el nombre: identificador uniforme de recursos). Con la primera convención, el recurso del que se habla cuando hace "GET / user / 123" y el recurso del que se habla cuando hace "DELETE / user / 123" es claramente el mismo recurso porque tienen la misma URL.

Con la segunda convención, no puede estar seguro de que "GET / user / 123" y "DELETE / user / delete / 123" en realidad son el mismo recurso, y parece implicar que está eliminando un recurso relacionado en lugar del recurso en sí mismo, por lo que sería bastante sorprendente que eliminar /user/delete/123realmente elimine /user/123. Si todas las operaciones funcionan en diferentes URL, el URI ya no funciona como un identificador de recursos.

Cuando dices DELETE /user/123, estás diciendo "eliminar 'registro de usuario con id 123'". Mientras que si dices DELETE /user/delete/123, lo que pareces estar insinuando es "borrar 'el registro de eliminación del usuario con la identificación 123'", que probablemente no sea lo que quieres decir. E incluso si usa el verbo más correcto en esta situación: "POST / user / delete / 123" que dice "hacer la operación adjunta a 'delegador de usuario con id 123'", sigue siendo una forma indirecta de eliminar un registro (Esto es similar a la nounificación de un verbo en inglés).

Una forma de pensar acerca de la URL es tratarla como punteros a objetos y recursos como objetos en la programación orientada a objetos. Cuando lo haga GET /user/123, DELETE /user/123, se puede pensar en pensar en ellos como métodos del objeto: [/user/123].get(), [/user/123].delete()donde el []es como un puntero dereferencing operador, sino para las direcciones URL (si usted sabe un idioma que tiene punteros). Uno de los principios subyacentes de REST es una interfaz uniforme, es decir, tener un conjunto pequeño y limitado de verbos / métodos que funcione para todo en una vasta red de recursos / objetos.

Por lo tanto, el primero es mejor.

PD: por supuesto, esto está mirando REST de la manera más pura. A veces, la practicidad es mejor que la pureza, y es necesario hacer concesiones para clientes con un cerebro muerto o un marco que dificulta hacer un DESCANSO adecuado.

Lie Ryan
fuente
+1 para el ejemplo de OOP :)
53777A
6

(lo siento, la primera vez que perdí el / edit / y / delete / in (2) ...)

La idea del URI es que es un identificador de un recurso direccionable , en lugar de una invocación de método . Entonces, el URI debe apuntar a un recurso específico. Y si hace referencia al URI, siempre debe obtener el mismo recurso.

Es decir, debe pensar en los URI de la misma manera que piensa en la Clave primaria de una fila en una base de datos. Identifica de manera única algo: Identificador Universal de Recursos.

Entonces, ya sea que use plural o singular, el URI debe ser un identificador en lugar de una invocación . Lo que está tratando de hacer va en el método, a saber: GET (obtener), PUT (crear / actualizar), DELETE (eliminar) o POST (todo lo demás).

Entonces "/ item / delete / 123" interrumpe REST porque no apunta a un recurso, es más una invocación de método.

(Además, semánticamente, debería poder OBTENER un URI, decidir que está desactualizado y luego ELIMINAR el mismo URI, porque es un identificador. Si el URI GET no tiene "/ delete /" y DELETE sí, entonces eso va en contra de la semántica HTTP. Estás transmitiendo 2 o más URI por recurso donde 1 lo hará).

Ahora, el truco es este: no hay una definición real clara de qué es y qué no es un recurso, por lo que la evasión común en REST es definir un "sustantivo de procesamiento" y señalar el URI a eso. Eso es más o menos un juego de palabras, pero satisface la semántica.

Entonces, si, por ejemplo, realmente no puede usar esto por alguna razón:

DELETE /items/123

podría declarar al mundo que tiene un recurso de procesamiento "deletor" y utilizar

POST /items/deletor  { id: 123 }

Ahora, eso se parece mucho a RPC (Llamada a procedimiento remoto), pero cae a través del gran vacío de la cláusula de "procesamiento de datos" de la especificación POST nombrada en la especificación HTTP.

Sin embargo, hacerlo es algo excepcional, y si puede usar el PUT común para crear / actualizar, BORRAR para eliminar y POST para agregar, crear y todo lo demás, entonces debería hacerlo , porque es un uso más estándar de HTTP. Pero si tiene un caso complicado como "confirmar" o "publicar" o "redactar", entonces el caso de usar un sustantivo de procesador satisface a los puristas de REST y aún le brinda la semántica que necesita.

Robar
fuente