ejemplo - API REST Mejores prácticas: ¿Dónde colocar los parámetros?
api vs api rest (14)
Una API REST puede tener parámetros de al menos dos formas:
- Como parte de la ruta URL (es decir,
/api/resource/parametervalue
) - Como un 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 utiliza 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 para poner estos parámetros en la ruta de la URL?
"Empaque" y publique sus datos en el "contexto" que proporciona el localizador de recursos del universo, que significa # 1 por el bien del localizador.
Cuidado con las limitaciones con # 2. Prefiero los POST al # 1.
nota: las limitaciones se discuten para
POST en ¿Hay un tamaño máximo para el contenido del parámetro POST?
GET in ¿Hay un límite a la longitud 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).
Aquí está mi opinión.
Los parámetros de consulta se utilizan como metadatos para una solicitud. Actúan como filtro o modificador de una llamada de recurso existente.
Ejemplo:
/calendar/2014-08-08/events
Debe dar los eventos del calendario para ese día.
Si quieres eventos para una categoría específica.
/calendar/2014-08-08/events?category=appointments
O si necesitas eventos de más de 30 minutos.
/calendar/2014-08-08/events?duration=30
Una prueba de fuego sería para comprobar si la solicitud todavía se puede servir sin un parámetro de consulta.
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, se agrega a la claridad y ofrece más extensibilidad. También me permite tener una lógica separada entre la creació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 partes específicas del usuario que son eliminadas así.
De acuerdo con 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 colocar 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 enrutar).
Por ejemplo:
-
/users/123
y/users/123?fields="name, age"
-
/users
y/users?name="John"&age=30
Para reducir el mapa me gusta usar los siguientes enfoques:
-
/users?name="John"&age=30
-
/users/name:John/age:30
Por lo tanto, depende realmente de usted (y del enrutador del lado del servidor) cómo construye sus URI.
Nota: Solo para mencionar estos parámetros son parámetros de consulta. Entonces, lo que realmente está haciendo es definir un lenguaje de consulta simple. Por 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 here son muy limitadas ...
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 de la cuestión del gusto y la intuición ...
Tomo el siguiente enfoque:
- url ruta-elemento: el recurso y su ruta-elemento forman un recorrido transversal de directorio y un sub-recurso (por ejemplo, / items / {id}, / users / items). Cuando no esté seguro, pregúntele a sus colegas, si piensan que atravesar y piensan en "otro directorio" lo más probable es que el elemento de ruta sea 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)
En mi opinión, los parámetros deberían ser mejores como argumentos de consulta. La url se utiliza para identificar el recurso, mientras que los parámetros de consulta agregados para especificar qué parte del recurso que desea, cualquier estado que el recurso debe tener, etc.
Es una pregunta muy interesante.
Puede usar ambos, no hay ninguna regla estricta sobre este tema, pero el uso de las 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 usan 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 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 jerárquica, aún puede usar las variables de ruta, usando coma o punto y coma:
/ Ciudad / longitud, latitud
Como regla general, use una coma cuando el orden de los parámetros es importante, use punto y coma cuando el orden no importa:
/ IconGenerator / rojo; azul; verde
Aparte de esas razones, hay algunos casos en los que es muy común utilizar variables de cadena de consulta:
- Cuando necesite que el navegador coloque automáticamente las 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 importante para usar uno de estos métodos, pero siempre que pueda, use las variables URI.
Generalmente tiendo hacia el # 2, como un argumento de consulta (es decir, / api / resource? Parámetro = valor).
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 elijas, asegúrate de elegir solo uno, no mezclar y combinar. Eso lleva a una API confusa.
No hay reglas duras y rápidas, pero la regla de oro desde un punto de vista puramente conceptual que me gusta usar puede resumirse brevemente de la siguiente manera: una ruta URI (por definición) representa un recurso y los parámetros de consulta son esencialmente modificadores en ese recurso. . Hasta ahora, lo más probable es que no ayude ... Con una API REST tiene los métodos principales de actuar sobre un solo recurso utilizando GET
, PUT
y DELETE
. Por lo tanto, si algo debe ser representado en la ruta o como un parámetro puede reducirse a si esos métodos tienen sentido para la representación en cuestión. ¿ PUT
razonablemente algo en ese camino y sería semánticamente correcto hacerlo? Por supuesto, puede PUT
algo en cualquier lugar y doblar el back-end para manejarlo, pero debe PUT
lo que equivale a una representación del recurso real y no a una versión contextualizada innecesariamente del mismo. Para las colecciones se puede hacer lo mismo con POST
. Si quisiera agregar a una colección particular, ¿cuál sería una URL que tiene sentido para POST
?
Esto todavía deja algunas áreas grises, ya que algunos caminos podrían indicar qué cantidad de recursos de los padres son hijos, lo cual es un tanto discrecional y depende de su uso. La única línea dura que dibuja es que cualquier tipo de representación transitiva debe hacerse usando 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 totalmente 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 objetos.
La adopción de este tipo de perspectiva orientada a los recursos puede fácilmente asignarse directamente al gráfico de objetos de su modelo de dominio e impulsar la lógica de su API hasta el punto en que todo funcione de manera muy clara y de manera bastante autodocumentada una vez que se ajuste a la claridad. El concepto también se puede aclarar alejándose de los sistemas que utilizan el enrutamiento tradicional de URL asignado a un modelo de datos que normalmente no se ajusta (es decir, un RDBMS). Apache Sling sería un buen lugar para comenzar. El concepto de envío de objetos en un sistema como Zope también proporciona un análogo más claro.
Respuesta tardía, pero agregaré información adicional a lo que se ha compartido, a saber, que hay varios tipos de "parámetros" en una solicitud, y debe tener esto en cuenta.
- Localizadores: por ejemplo, identificadores de recursos como ID o acción / vista
- Filtros: por ejemplo, los parámetros que proporcionan una búsqueda, ordenan o reducen el conjunto de resultados.
- Estado - Ej. Identificación de sesión, api keys, whatevs.
- Contenido - Por ejemplo, datos a almacenar.
Ahora veamos los diferentes lugares donde podrían ir estos parámetros.
- Solicitar encabezados y cookies
- Cadena de consulta de URL ("GET" vars)
- Rutas de URL
- Cuerpo consulta cadena / multiparte ("POST" vars)
En general, desea que el estado se establezca en encabezados o cookies, según el tipo de información de estado que sea. Creo que todos podemos estar de acuerdo en esto. Use encabezados http personalizados (X-My-Header) si lo necesita.
De manera similar, el Contenido solo tiene un lugar al que pertenecer, que está en el cuerpo de la solicitud, ya sea como cadenas de consulta o como contenido de multipartas http y / o JSON. Esto es consistente con lo que recibe del servidor cuando le envía contenido. Así que no deberías ser grosero y hacerlo de manera diferente.
Los localizadores como "id = 5" o "action = refresh" o "page = 2" tendrían sentido como una ruta URL, como mysite.com/article/5/page=2
donde en parte sabes qué es cada parte se supone que significa (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 de la URI. Pueden estar en la forma de page=2
, o page/2
si sabe que después de cierto punto en el URI, las "carpetas" son valores-clave emparejados.
Los filtros siempre van en la cadena de consulta, porque mientras forman parte de la búsqueda de los datos correctos, solo están allí para devolver un subconjunto o modificación de lo que devuelven los Localizadores. La búsqueda en mysite.com/article/?query=Obama
(subconjunto) es un filtro, y también lo es /article/5?order=backwards
mysite.com/article/?query=Obama
/article/5?order=backwards
(modificación). ¡Piensa en lo que hace, no solo en lo que se llama!
Si "vista" 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 veremos ( mysite.com/article/5/view=summary
), entonces es un localizador.
Recuerda, restringir un conjunto de recursos se está filtrando. Localizar algo específico dentro de un recurso es localizar ... 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 tal modificación).
¡Espero que esto haya ayudado a dar a la gente algunos momentos de eureka si se han perdido sobre dónde poner las cosas!
Según la implementación 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á 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 = géneros que devolverán los datos de géneros para esa canción en particular.
Si hay buenas prácticas documentadas, todavía no las he encontrado. Sin embargo, aquí hay algunas pautas que utilizo para determinar dónde colocar los 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, tenderé hacia un parámetro de segmento de ruta. por ejemplo, /customer/232
donde 232 no es un ID de cliente válido.
Sin embargo, si desea devolver una lista vacía, cuando no se encuentre el parámetro, sugiero utilizar parámetros de cadena de consulta. 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 /en/document/foo.txt
Prefiero que los identificadores únicos estén 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 here . También hay otra especificación RFC muy útil here que define reglas para parametrizar URIs.
Una "dimensión" de este tema se ha omitido, pero es muy importante: hay veces 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:
Muchas aplicaciones web actualmente implementan la arquitectura MVC (Modelo, Vista, Controlador). Asumen que se proporciona una determinada ruta estándar, incluso 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 (mostrando la página de ofertas especiales)list-all
es la acción del controlador o el nombre de la función a llamar. (*)limit = 25 es una opción, que indica que se mostrarán 25 elementos por página.
(*) list-all
es 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 de index
predeterminada, implícita (y generalmente omitida en la URL) que se llama cuando el usuario desea que se realice una acción predeterminada. Así que la URL del mundo real sería:
http://www.domain.tld/special-offers?limit=25
Con una estructura de aplicación o frameworkd ahora bastante estándar similar a la anterior, a menudo obtendrá un servidor web que está optimizado para ella, que vuelve a escribir las URL (la verdadera "URL sin SEO" sería: http://www.domain.tld/index.php?route=special-offers/list-all&limit=25
).
Por lo tanto, usted, como desarrollador, se enfrenta a lidiar con la infraestructura existente y adapta 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 (¡esta última 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 consistencia como por su facilidad / velocidad (y por lo tanto, el ahorro de presupuesto).
Para volver al ejemplo práctico anterior, una API REST coherente sería algo con URL como:
http://www.domain.tld/api/special-offers-list?from=15&limit=25
o (URLs 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".
Veo muchas API REST que no manejan bien los parámetros. Un ejemplo que aparece 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 de corolario es cuándo un parámetro no debería ser un parámetro, sino que debería moverse al HEADER o BODY de la solicitud.