Mejores prácticas de API REST: ¿Dónde poner los parámetros? [cerrado]

348

Una API REST puede tener parámetros al menos de dos maneras:

  1. Como parte de la ruta URL (es decir /api/resource/parametervalue )
  2. Como argumento de consulta (es decir /api/resource?parameter=value )

¿Cuál es la mejor práctica aquí? ¿Existen pautas generales sobre cuándo usar 1 y cuándo usar 2?

Ejemplo del mundo real: Twitter usa parámetros de consulta para especificar intervalos. ( http://api.twitter.com/1/statuses/home_timeline.json?since_id=12345&max_id=54321)

¿Se consideraría un mejor diseño colocar estos parámetros en la ruta URL?

Kalle Gustafsson
fuente

Respuestas:

254

Si hay mejores prácticas documentadas, aún no las he encontrado. Sin embargo, aquí hay algunas pautas que uso al determinar dónde colocar parámetros en una url:

Los parámetros opcionales tienden a ser más fáciles de colocar en la cadena de consulta.

Si desea devolver un error 404 cuando el valor del parámetro no corresponde a un recurso existente, entonces tendería hacia un parámetro de segmento de ruta. por ejemplo, /customer/232donde 232 no es una identificación de cliente válida.

Sin embargo, si desea devolver una lista vacía, cuando no se encuentre el parámetro, sugiero usar parámetros de cadena de consulta. p.ej/contacts?name=dave

Si un parámetro afecta a un subárbol completo de su espacio URI, use un segmento de ruta. por ejemplo, un parámetro de idioma /en/document/foo.txt versus/document/foo.txt?language=en

Prefiero identificadores únicos para estar en un segmento de ruta en lugar de un parámetro de consulta.

Las reglas oficiales para URI se encuentran en esta especificación RFC aquí . También hay otra especificación RFC muy útil aquí que define reglas para parametrizar URI.

Darrel Miller
fuente
55
¡Los URI de la regla oficial y el borrador del sepc fueron realmente útiles e interesantes! :-)
KajMagnus
1
La prueba de error 404 me ayuda mucho a evitar poner información en la ruta que pertenece a los parámetros de consulta, encabezados o el cuerpo de la solicitud. Gracias por señalar eso!
Kevin Condon
152

Respuesta tardía, pero agregaré información adicional a lo que se ha compartido, a saber, que hay varios tipos de "parámetros" para una solicitud, y debe tener esto en cuenta.

  1. Localizadores: por ejemplo, identificadores de recursos, como ID o acción / vista
  2. Filtros: por ejemplo, parámetros que proporcionan una búsqueda, clasificación o reducción del conjunto de resultados.
  3. Estado: por ejemplo, identificación de sesión, claves de API, lo que sea.
  4. Contenido: por ejemplo, datos que se almacenarán.

Ahora veamos los diferentes lugares donde podrían ir estos parámetros.

  1. Solicitar encabezados y cookies
  2. Cadena de consulta de URL (vars "GET")
  3. Rutas URL
  4. Body query string / multipart (vars "POST")

En general, desea que el estado se configure en encabezados o cookies, según el tipo de información de estado que sea. Creo que todos podemos estar de acuerdo en esto. Utilice encabezados http personalizados (X-My-Header) si es necesario.

Del mismo modo, el Contenido solo tiene un lugar para pertenecer, que está en el cuerpo de la solicitud, ya sea como cadenas de consulta o como contenido http multipart y / o JSON. Esto es consistente con lo que recibe del servidor cuando le envía contenido. Así que no debes ser grosero y hacerlo de manera diferente.

Los localizadores como "id = 5" o "action = refresh" o "page = 2" tendrían sentido tener como una ruta URL, como mysite.com/article/5/page=2donde en parte sabes lo que se supone que significa cada parte (los conceptos básicos como el artículo y 5 obviamente significa obtener los datos del tipo de artículo con id 5) y los parámetros adicionales se especifican como parte del URI. Pueden tener la forma de page=2, o page/2si sabe que después de cierto punto en el URI, las "carpetas" son pares de valores clave.

Los filtros siempre van en la cadena de consulta, porque si bien son parte de la búsqueda de los datos correctos, solo están allí para devolver un subconjunto o modificación de lo que los localizadores devuelven solos. La búsqueda en mysite.com/article/?query=Obama(subconjunto) es un filtro, y también lo es /article/5?order=backwards(modificación). ¡Piensa en lo que hace, no solo en cómo se llama!

Si "ver" determina el formato de salida, entonces es un filtro ( mysite.com/article/5?view=pdf) porque devuelve una modificación del recurso encontrado en lugar de buscar en qué recurso queremos. Si en cambio decide qué parte específica del artículo podemos ver ( mysite.com/article/5/view=summary), entonces es un localizador.

Recuerde, reducir un conjunto de recursos es filtrar. Localizar algo específico dentro de un recurso es ubicar ... duh. El filtrado de subconjuntos puede devolver cualquier número de resultados (incluso 0). La localización siempre encontrará esa instancia específica de algo (si existe). El filtrado de modificaciones devolverá los mismos datos que el localizador, excepto los modificados (si se permite dicha modificación).

¡Espero que esto haya ayudado a las personas a tener momentos eureka si se han perdido sobre dónde poner las cosas!

Tor Valamo
fuente
2
¿Por qué no hay idun filtro entonces? Devuelve un subconjunto del recurso
Jonathan.
13
@ Jonathan. no, devuelve un recurso específico, es decir, el número de artículo 5. Un filtro siempre es una forma de limitar una búsqueda en una colección de recursos. Si desea solo ese recurso específico, entonces debe haber una forma designada para obtenerlo. Filtrar significa que tiene la posibilidad de devolver múltiples recursos. Una ID no es un filtro, es un recurso único definido. Si tuviera una GAMA de ID, entonces sería un filtro, incluso si el rango solo incluye una ID. Si el filtro también incluye tipos de recursos, devolvería todos los recursos con ID 5, no solo el artículo.
Tor Valamo
1
@ Jonathan: como mencionó DarrelMiller, esperaría que una solicitud en object / id devuelva 404 en caso de identificación desconocida, mientras que esperaría que object? Id = id regrese y la lista vacía. Además, consideraría que cualquier tipo de filtrado / subconjunto debería devolver una lista.
njzk2
1
Las páginas son difíciles porque, como usted dice, puede ser un filtro de un recurso (colección de páginas), pero al mismo tiempo es un recurso específico dentro de esa colección. Siempre solicitaría una página de artículo por localizador, no por filtro. Sin embargo, la página puede ser un filtro de una lista de algo, digamos una lista de usuarios. Pero la página es inherentemente un delimitador, también conocido como "comenzar en el elemento (page-1)*perpagey mostrar perpageelementos". Usarlo como filtro es correcto entonces, pero por diferentes razones. Llamarlo "página" es técnicamente incorrecto. Más semánticamente correcto sería llamarlo "desde" o "startAt"
Tor Valamo
1
(continuación) El significado semántico de "página" es que es un recurso específico que no cambia. Viene de la impresión física. Si nunca tuviéramos libros o material impreso, "página" realmente no sería una palabra. Si tiene una lista dinámica de elementos, dividida en "páginas", realmente debe proporcionar un punto de partida específico, ya sea numérico, alfabético o incluso específico del elemento, así como un filtro de "cuántos por página". Si quiero hacer referencia a algo en su lista, quiero detalles. Además, no quiero ir a la página 5 solo para darme cuenta de que ahora ha cambiado el interno perpagea 50 en lugar de 20.
Tor Valamo
21

Depende de un diseño. No hay reglas para los URI en REST a través de HTTP (lo principal es que son únicos). A menudo se trata del gusto y la intuición ...

Tomo el siguiente enfoque:

  • url path-element: el recurso y su path-element forma un recorrido de directorio y un subrecurso (por ejemplo, / items / {id}, / users / items). Cuando no esté seguro, pregunte a sus colegas, si piensan que el recorrido transversal y piensan en "otro directorio", el elemento de ruta más probable es la opción correcta
  • parámetro url: cuando realmente no hay recorrido (los recursos de búsqueda con múltiples parámetros de consulta son un buen ejemplo para eso)
manuel aldana
fuente
1
En realidad, hay reglas bastante claras sobre cómo se supone que debe verse un URI, y muy poca ambigüedad sobre cómo aplicarlas a los URI RESTful.
DanMan
18

OMI, los parámetros deberían ser mejores como argumentos de consulta. La url se usa para identificar el recurso, mientras que los parámetros de consulta agregados para especificar qué parte del recurso desea, cualquier estado que el recurso debería tener, etc.

PeterWong
fuente
77
En realidad, tanto la ruta como la consulta se usan en combinación para identificar el recurso. Esto se aclaró en RFC 3986 http://labs.apache.org/webarch/uri/rfc/rfc3986.html#query
Darrel Miller
@DarrelMiller Sé que esta es una publicación antigua, pero estoy interesado en saber más sobre los parámetros de consulta de hechos que también se utilizan para identificar el recurso. El enlace que proporcionó ahora está muerto. He examinado RFC3986 pero no veo cómo dedujo este hecho. Además, por definición, los parámetros de un identificador no deberían ser opcionales, por lo que no parece apropiado utilizar parámetros de consulta para la identificación.
Mickael Marrache
@MickaelMarrache Vea la primera línea en la sección 3.4 tools.ietf.org/html/rfc3986#section-3.4
Darrel Miller
2
@DarrelMiller ¡Gracias! Mi pregunta proviene del hecho de que, en general, los componentes HTTP intermedios no almacenan en caché las respuestas de las solicitudes que contienen una cadena de consulta. Por lo tanto, parece que los parámetros de consulta son más apropiados para buscar recursos de acuerdo con algunos criterios y no para identificar un recurso de forma exclusiva.
Mickael Marrache
17

Según la implementación de REST,

1) Las variables de ruta se utilizan para la acción directa sobre los recursos, como un contacto o una canción, por ejemplo.
GET etc / api / resource / {songid} o
GET etc / api / resource / {contactid} devolverán los datos respectivos.

2) Los permisos / argumentos de consulta se utilizan para los recursos directos como los metadatos de una canción, por ejemplo, GET / api / resource / {songid}? Metadata = generes; devolverá los datos de los géneros para esa canción en particular.

Satish Bellapu
fuente
55
En realidad no hay un estándar REST . Según Wikipedia : a diferencia de los servicios web basados ​​en SOAP, no existe un estándar "oficial" para las API web RESTful. [14] Esto se debe a que REST es un estilo arquitectónico, a diferencia de SOAP, que es un protocolo. Aunque REST no es un estándar, una implementación RESTful como la Web puede usar estándares como HTTP, URI, XML, etc.
DavidRR
No me gusta el enfoque 2. Prefiero preferir / api / genres? Songid = 123 o / api / songs / {song-id} / generes
Bart Calixto
1
@Bart, Satish se refería a Variables en el camino, que es esencialmente a lo que te refieres como tu preferencia ... sin embargo, si los géneros son realmente metadatos, y no un campo de la entidad / recurso de la canción ... entonces podría ver más sensibilidad al usar una cadena de consulta en él ..
Brett Caswell
¡@BrettCaswell lo consiguió! Gracias por mencionarlo. ¡realmente lo aprecio!
Bart Calixto
16

"Empaque" y PUBLICE sus datos en el "contexto" que proporciona el localizador de recursos del universo, lo que significa # 1 por el bien del localizador.

Tenga en cuenta las limitaciones con el n. ° 2. Prefiero POSTs a # 1.

nota: las limitaciones se discuten para

POST in ¿Hay un tamaño máximo para el contenido del parámetro POST?

GET in ¿Hay un límite en la duración de una solicitud GET? y tamaño máximo de los parámetros de URL en _GET

ps estos límites se basan en las capacidades del cliente (navegador) y el servidor (configuración).

dgm
fuente
Complemento: las rutas ingeniosas pueden tener versiones (distinguidas mediante encabezados), por lo que proporcionan una funcionalidad evolucionada sin necesidad de alterar el código que consume el código rest-full (api) que escribe como en restify -> busque rutas versionadas
dgm
5

Según el estándar URI, la ruta es para parámetros jerárquicos y la consulta es para parámetros no jerárquicos. De c. puede ser muy subjetivo lo que es jerárquico para ti.

En situaciones en las que se asignan múltiples URI al mismo recurso, me gusta poner los parámetros, necesarios para la identificación, en la ruta y los parámetros, necesarios para construir la representación, en la consulta. (Para mí de esta manera es más fácil de enrutar).

Por ejemplo:

  • /users/123 y /users/123?fields="name, age"
  • /users y /users?name="John"&age=30

Para reducir mapa me gusta usar los siguientes enfoques:

  • /users?name="John"&age=30
  • /users/name:John/age:30

Por lo tanto, depende de usted (y del enrutador del lado del servidor) cómo construye sus URI.

nota: Solo por mencionar que estos parámetros son parámetros de consulta. Entonces, lo que realmente está haciendo es definir un lenguaje de consulta simple. Mediante consultas complejas (que contienen operadores como y, o, mayor que, etc.) le sugiero que utilice un lenguaje de consulta ya existente. Las capacidades de las plantillas URI son muy limitadas ...

inf3rno
fuente
4

Como programador a menudo en el extremo del cliente, prefiero el argumento de consulta. Además, para mí, separa la ruta URL de los parámetros, agrega claridad y ofrece más extensibilidad. También me permite tener una lógica separada entre la construcción de URL / URI y el generador de parámetros.

Me gusta lo que dijo manuel aldana sobre la otra opción si hay algún tipo de árbol involucrado. Puedo ver que las partes específicas del usuario se cortan así.

Joe Plante
fuente
4

No hay reglas duras y rápidas, pero la regla general desde un punto de vista puramente conceptual que me gusta usar se puede resumir brevemente de esta manera: una ruta URI (por definición) representa un recurso y los parámetros de consulta son esencialmente modificadores en ese recurso . Hasta el momento, que probablemente no ayuda ... Con una API REST que tiene los principales métodos de actuar sobre el uso de un único recurso GET, PUTy DELETE. Por lo tanto, si algo debe representarse en la ruta o como parámetro puede reducirse a si esos métodos tienen sentido para la representación en cuestión. ¿Podría razonablemente hacer PUTalgo en ese camino y sería semánticamente correcto hacerlo? Por supuesto, podría hacer PUTalgo en cualquier lugar y doblar el back-end para manejarlo, pero debería serPUTEsto equivale a una representación del recurso real y no a una versión innecesariamente contextualizada del mismo. Para colecciones, se puede hacer lo mismo POST. Si desea agregar a una colección en particular, cuál sería una URL que tenga sentido POST.

Esto todavía deja algunas áreas grises, ya que algunos caminos podrían apuntar a la cantidad de recursos de los padres que es algo discrecional y dependiente de su uso. La única línea difícil que esto dibuja es que cualquier tipo de representación transitiva debe hacerse utilizando un parámetro de consulta, ya que no tendría un recurso subyacente.

En respuesta al ejemplo del mundo real dado en la pregunta original (API de Twitter), los parámetros representan una consulta transitiva que filtra el estado de los recursos (en lugar de una jerarquía). En ese ejemplo en particular, sería completamente irrazonable agregar a la colección representada por esas restricciones, y además esa consulta no podría representarse como una ruta que tendría sentido en los términos de un gráfico de objeto.

La adopción de este tipo de perspectiva orientada a los recursos puede mapearse fácilmente directamente al gráfico de objetos de su modelo de dominio y conducir la lógica de su API al punto en que todo funciona de manera muy limpia y de manera bastante autodocumentada una vez que se aclara. El concepto también se puede aclarar alejándose de los sistemas que utilizan el enrutamiento de URL tradicional asignado a un modelo de datos que normalmente no encaja (es decir, un RDBMS). Apache Sling ciertamente sería un buen lugar para comenzar. El concepto de despacho transversal de objetos en un sistema como Zope también proporciona un análogo más claro.

Matt Whipple
fuente
4

Aquí está mi opinión.

Los parámetros de consulta se utilizan como metadatos para una solicitud. Actúan como filtro o modificador para una llamada de recurso existente.

Ejemplo:

/calendar/2014-08-08/events

debería dar eventos de calendario para ese día.

Si quieres eventos para una categoría específica

/calendar/2014-08-08/events?category=appointments

o si necesita eventos de más de 30 minutos

/calendar/2014-08-08/events?duration=30

Una prueba de fuego sería verificar si la solicitud aún se puede servir sin parámetros de consulta.

Arrendajo
fuente
2

Por lo general, tiendo a # 2, como argumento de consulta (es decir, / api / resource? Parameter = value).

Una tercera opción es publicar el parámetro = valor en el cuerpo.

Esto se debe a que funciona mejor para recursos de múltiples parámetros y es más extensible para uso futuro.

No importa cuál elija, asegúrese de elegir solo uno, no mezcle y combine. Eso lleva a una API confusa.

NorthIsUp
fuente
2

Se ha dejado de lado una "dimensión" de este tema, pero es muy importante: hay momentos en que las "mejores prácticas" tienen que ponerse de acuerdo con la plataforma que estamos implementando o aumentando con las capacidades REST.

Ejemplo práctico:

En la actualidad, muchas aplicaciones web implementan la arquitectura MVC (Modelo, Vista, Controlador). Asumen que se proporciona una ruta estándar determinada, aún más cuando esas aplicaciones web vienen con la opción "Habilitar URL de SEO".

Solo por mencionar una aplicación web bastante famosa: una tienda de comercio electrónico OpenCart. Cuando el administrador habilita las "URL de SEO", espera que dichas URL vengan en un formato MVC bastante estándar como:

http://www.domain.tld/special-offers/list-all?limit=25

Dónde

  • special-offers es el controlador MVC que procesará la URL (que muestra la página de ofertas especiales)

  • list-alles la acción del controlador o el nombre de la función a llamar. (*)

  • limit = 25 es una opción, indicando que se mostrarán 25 elementos por página.

(*) list-alles un nombre de función ficticio que utilicé para mayor claridad. En realidad, OpenCart y la mayoría de los marcos MVC tienen una función predeterminada, implícita (y generalmente omitida en la URL) indexque se llama cuando el usuario desea que se realice una acción predeterminada. Entonces la URL del mundo real sería:

http://www.domain.tld/special-offers?limit=25

Con una aplicación ahora bastante estándar o una estructura framework similar a la anterior, a menudo obtendrá un servidor web que está optimizado para ello, que reescribe las URL para él (la verdadera "URL no SEOed" sería:) http://www.domain.tld/index.php?route=special-offers/list-all&limit=25.

Por lo tanto, usted, como desarrollador, debe enfrentarse a la infraestructura existente y adaptar sus "mejores prácticas", a menos que sea el administrador del sistema, sepa exactamente cómo modificar una configuración de reescritura de Apache / NGinx (¡esto último puede ser desagradable!) Y así en.

Por lo tanto, su API REST a menudo sería mucho mejor siguiendo los estándares de la aplicación web de referencia, tanto por su coherencia como por su facilidad / velocidad (y, por lo tanto, el ahorro de presupuesto).

Para volver al ejemplo práctico anterior, una API REST consistente sería algo con URL como:

http://www.domain.tld/api/special-offers-list?from=15&limit=25

o (URL no SEO)

http://www.domain.tld/index.php?route=api/special-offers-list?from=15&limit=25

con una mezcla de argumentos de "caminos formados" y argumentos de "consulta formada".

Dario Fumagalli
fuente
1

Veo muchas API REST que no manejan bien los parámetros. Un ejemplo que surge a menudo es cuando el URI incluye información de identificación personal.

http://software.danielwatrous.com/design-principles-for-rest-apis/

Creo que una pregunta del corolario es cuando un parámetro no debería ser un parámetro, sino que debería moverse al HEADER o BODY de la solicitud.

Daniel Watrous
fuente
0

Es una pregunta muy interesante.

Puede usar ambos, no hay ninguna regla estricta sobre este tema, pero el uso de variables de ruta URI tiene algunas ventajas:

  • Caché : la mayoría de los servicios de caché web en Internet no almacenan en caché la solicitud GET cuando contienen parámetros de consulta. Lo hacen porque hay muchos sistemas RPC que utilizan solicitudes GET para cambiar los datos en el servidor (¡¡¡¡falla !! Get debe ser un método seguro)

Pero si usa variables de ruta, todos estos servicios pueden almacenar en caché sus solicitudes GET.

  • Jerarquía : las variables de ruta pueden representar la jerarquía: / Ciudad / Calle / Lugar

Le da al usuario más información sobre la estructura de los datos.

Pero si sus datos no tienen ninguna relación de jerarquía, aún puede usar las variables de ruta, usando una coma o punto y coma:

/ Ciudad / longitud, latitud

Como regla general, use una coma cuando el orden de los parámetros sea importante, use punto y coma cuando el orden no sea importante:

/ IconGenerator / rojo; azul; verde

Además de esas razones, hay algunos casos en los que es muy común usar variables de cadena de consulta:

  • Cuando necesite que el navegador coloque automáticamente variables de formulario HTML en el URI
  • Cuando se trata de algoritmo. Por ejemplo, el motor de Google utiliza cadenas de consulta:

http: // www.google.com/search?q=rest

En resumen, no hay ninguna razón sólida para usar uno de estos métodos, pero siempre que pueda, use variables URI.

jfcorugedo
fuente