Mejores prácticas de API REST: cómo aceptar una lista de valores de parámetros como entrada [cerrado]
Estamos lanzando una nueva API REST y quería recibir comentarios de la comunidad sobre las mejores prácticas sobre cómo deberíamos formatear los parámetros de entrada:
En este momento, nuestra API está muy centrada en JSON (solo devuelve JSON). El debate sobre si queremos/necesitamos devolver XML es un tema aparte.
Como nuestra salida de API está centrada en JSON, hemos estado siguiendo un camino en el que nuestras entradas están un poco centradas en JSON y he estado pensando que puede ser conveniente para algunos, pero extraño en general.
Por ejemplo, para obtener algunos detalles del producto donde se pueden extraer varios productos a la vez, actualmente tenemos:
http://our.api.com/Product?id=["101404","7267261"]
¿Deberíamos simplificar esto como:
http://our.api.com/Product?id=101404,7267261
¿O es útil tener entrada JSON? ¿Más dolor?
Es posible que queramos aceptar ambos estilos, pero ¿esa flexibilidad realmente causa más confusión y dolores de cabeza (mantenibilidad, documentación, etc.)?
Un caso más complejo es cuando queremos ofrecer insumos más complejos. Por ejemplo, si queremos permitir múltiples filtros en la búsqueda:
http://our.api.com/Search?term=pumas&filters={"productType":["Clothing","Bags"],"color":["Black","Red"]}
No necesariamente queremos poner los tipos de filtro (por ejemplo, tipo de producto y color) como nombres de solicitud como este:
http://our.api.com/Search?term=pumas&productType=["Clothing","Bags"]&color=["Black","Red"]
Porque queríamos agrupar todas las entradas de filtro.
Al final, ¿esto realmente importa? Es posible que existan tantas utilidades JSON que el tipo de entrada simplemente no importe mucho.
Sé que nuestros clientes de JavaScript que realizan llamadas AJAX a la API pueden apreciar las entradas JSON para hacerles la vida más fácil.
Un paso atrás
En primer lugar, REST describe un URI como una identificación universalmente única. Demasiadas personas quedan atrapadas en la estructura de los URI y en qué URI son más "tranquilos" que otros. Este argumento es tan ridículo como decir que nombrar a alguien "Bob" es mejor que llamarlo "Joe"; ambos nombres cumplen la función de "identificar a una persona". Un URI no es más que un nombre universalmente único .
Entonces, a los ojos de REST, discutir sobre si ?id=["101404","7267261"]
es más relajante ?id=101404,7267261
o \Product\101404,7267261
algo inútil.
Ahora bien, dicho esto, muchas veces la forma en que se construyen los URI puede servir como un buen indicador para otros problemas en un servicio RESTful. Hay un par de señales de alerta en sus URI y preguntas en general.
Sugerencias
Múltiples URI para el mismo recurso y
Content-Location
Es posible que queramos aceptar ambos estilos, pero ¿esa flexibilidad realmente causa más confusión y dolores de cabeza (mantenibilidad, documentación, etc.)?
Los URI identifican recursos. Cada recurso debe tener un URI canónico. Esto no significa que no se puedan tener dos URI que apunten al mismo recurso , pero existen formas bien definidas de hacerlo. Si decide utilizar tanto el formato JSON como el basado en listas (o cualquier otro formato), debe decidir cuál de estos formatos es el URI canónico principal . Todas las respuestas a otros URI que apunten al mismo "recurso" deben incluir el
Content-Location
encabezado .Siguiendo con la analogía del nombre, tener múltiples URI es como tener apodos para las personas. Es perfectamente aceptable y, a menudo, bastante útil; sin embargo, si uso un apodo, probablemente aún quiera saber su nombre completo, la forma "oficial" de referirme a esa persona. De esta manera, cuando alguien menciona a alguien por su nombre completo, "Nichloas Telsa", sé que está hablando de la misma persona a la que me refiero como "Nick".
"Buscar" en tu URI
Un caso más complejo es cuando queremos ofrecer insumos más complejos. Por ejemplo, si queremos permitir múltiples filtros en la búsqueda...
Una regla general que uso es que si su URI contiene un verbo, puede ser una indicación de que algo anda mal. Los URI identifican un recurso, sin embargo, no deben indicar qué le estamos haciendo a ese recurso. Ese es el trabajo de HTTP o, en términos más sencillos, nuestra "interfaz uniforme".
Para superar la analogía del nombre, usar un verbo en un URI es como cambiar el nombre de alguien cuando quieres interactuar con esa persona. Si estoy interactuando con Bob, el nombre de Bob no se convierte en "BobHola" cuando quiero saludarlo. De manera similar, cuando queremos "buscar" Productos, nuestra estructura de URI no debería cambiar de "/Producto/..." a "/Buscar/...".
Respondiendo a su pregunta inicial
Con respecto a
["101404","7267261"]
vs101404,7267261
: Mi sugerencia aquí es evitar la sintaxis JSON por razones de simplicidad (es decir, no requiera que sus usuarios codifiquen URL cuando en realidad no es necesario). Hará que su API sea un poco más utilizable. Mejor aún, como otros han recomendado, opte por elapplication/x-www-form-urlencoded
formato estándar, ya que probablemente será más familiar para los usuarios finales (p. ej.?id[]=101404&id[]=7267261
). Puede que no sea "bonito", pero los URI bonitos no necesariamente significan URI utilizables. Sin embargo, para reiterar mi punto inicial, en última instancia, cuando se habla de REST, no importa. No te detengas demasiado en ello.Su ejemplo de URI de búsqueda compleja se puede resolver de manera muy similar al ejemplo de su producto. Recomendaría
application/x-www-form-urlencoded
volver a utilizar el formato, ya que es un estándar con el que muchos están familiarizados. Además, recomendaría fusionar los dos.
Tu URI...
/Search?term=pumas&filters={"productType":["Clothing","Bags"],"color":["Black","Red"]}
Su URI después de ser codificado como URI...
/Search?term=pumas&filters=%7B%22productType%22%3A%5B%22Clothing%22%2C%22Bags%22%5D%2C%22color%22%3A%5B%22Black%22%2C%22Red%22%5D%7D
Se puede transformar en...
/Product?term=pumas&productType[]=Clothing&productType[]=Bags&color[]=Black&color[]=Red
Además de evitar el requisito de codificación de URL y hacer que las cosas parezcan un poco más estándar, ahora homogeneiza un poco la API. El usuario sabe que si quiere recuperar un Producto o una Lista de Productos (ambos se consideran un único "recurso" en términos RESTful), está interesado en los /Product/...
URI.
La forma estándar de pasar una lista de valores como parámetros de URL es repetirlos:
http://our.api.com/Product?id=101404&id=7267261
La mayoría del código de servidor interpretará esto como una lista de valores, aunque muchos tienen simplificaciones de un solo valor, por lo que es posible que tengas que buscar.
Los valores delimitados también están bien.
Si necesita enviar JSON al servidor, no me gusta verlo en la URL (que es un formato diferente). En particular, las URL tienen una limitación de tamaño (en la práctica, aunque no en teoría).
La forma en que he visto a algunos hacer una consulta complicada RESTfully es en dos pasos:
POST
sus requisitos de consulta, recibiendo una identificación (esencialmente creando un recurso de criterios de búsqueda)GET
la búsqueda, haciendo referencia al ID anterior- Opcionalmente, BORRAR los requisitos de consulta si es necesario, pero tenga en cuenta que los requisitos están disponibles para su reutilización.
Primero:
Creo que puedes hacerlo de 2 maneras.
http://our.api.com/Product/<id>
: si solo quieres un registro
http://our.api.com/Product
: si quieres todos los registros
http://our.api.com/Product/<id1>,<id2>
: como sugirió James, puede ser una opción ya que lo que viene después de la etiqueta del Producto es un parámetro
O el que más me gusta es:
Puede utilizar la propiedad Hypermedia como motor de estado de aplicación (HATEOAS) de un RestFul WS y realizar una llamada http://our.api.com/Product
que debería devolver las URL equivalentes http://our.api.com/Product/<id>
y llamarlas después de esto.
Segundo
Cuando hay que hacer consultas sobre la url llamadas. Sugeriría usar HATEOAS nuevamente.
1) Haz una llamada ahttp://our.api.com/term/pumas/productType/clothing/color/black
2) Haz una llamada ahttp://our.api.com/term/pumas/productType/clothing,bags/color/black,red
3) (Usando HATEOAS) Haga una llamada a ` http://our.api.com/term/pumas/productType/ -> reciba las URL de todas las URL posibles de ropa -> llame a las que desee (ropa y bolsos) - > recibe las posibles URL de colores -> llama a las que quieras
Es posible que desee consultar RFC 6570 . Esta especificación de plantilla de URI muestra muchos ejemplos de cómo las URL pueden contener parámetros.
Primer caso:
Una búsqueda normal de productos se vería así
http://our.api.com/product/1
Entonces estoy pensando que la mejor práctica sería que hicieras esto.
http://our.api.com/Product/101404,7267261
Segundo caso
Buscar con parámetros de cadena de consulta, así de bien. Me sentiría tentado a combinar términos con AND y OR en lugar de usar []
.
PD: Esto puede ser subjetivo, así que haz lo que te resulte más cómodo.
La razón para colocar los datos en la URL es para que el enlace pueda pegarse en un sitio o compartirse entre usuarios. Si esto no es un problema, utilice JSON/POST en su lugar.
EDITAR: Pensándolo bien, creo que este enfoque se adapta a una entidad con una clave compuesta, pero no a una consulta para múltiples entidades.
Estaré del lado de la respuesta de Nategood, ya que está completa y parece satisfacer sus necesidades. Sin embargo, me gustaría agregar un comentario sobre cómo identificar múltiples (1 o más) recursos de esa manera:
http://our.api.com/Product/101404,7267261
Al hacerlo, usted:
Complejique a los clientes
obligándolos a interpretar su respuesta como una matriz, lo que para mí es contrario a la intuición si hago la siguiente solicitud:http://our.api.com/Product/101404
Cree API redundantes con una API para obtener todos los productos y la anterior para obtener 1 o muchos. Dado que no debes mostrar más de 1 página de detalles a un usuario por el bien de la UX, creo que tener más de 1 ID sería inútil y se usaría únicamente para filtrar los productos.
Puede que no sea tan problemático, pero tendrá que manejar esto usted mismo del lado del servidor devolviendo una sola entidad (verificando si su respuesta contiene una o más) o dejar que los clientes la administren.
Ejemplo
Quiero pedir un libro de Amazing . Sé exactamente qué libro es y lo veo en la lista cuando busco libros de terror:
- 10 000 líneas asombrosas, 0 pruebas asombrosas
- El regreso del asombroso monstruo
- Duplicamos un código asombroso
- El sorprendente comienzo del fin.
Después de seleccionar el segundo libro, se me redirige a una página que detalla la parte del libro de una lista:
--------------------------------------------
Book #1
--------------------------------------------
Title: The return of the amazing monster
Summary:
Pages:
Publisher:
--------------------------------------------
¿O en una página que me brinda todos los detalles de ese libro únicamente?
---------------------------------
The return of the amazing monster
---------------------------------
Summary:
Pages:
Publisher:
---------------------------------
Mi opinión
Sugeriría usar el ID en la variable de ruta cuando se garantiza la unicidad al obtener los detalles de este recurso. Por ejemplo, las API siguientes sugieren varias formas de obtener los detalles de un recurso específico (suponiendo que un producto tenga un ID único y una especificación para ese producto tenga un nombre único y pueda navegar de arriba hacia abajo):
/products/{id}
/products/{id}/specs/{name}
En el momento en que necesite más de un recurso, le sugeriría filtrar desde una colección más grande. Para el mismo ejemplo:
/products?ids=
Por supuesto, esta es mi opinión ya que no es impuesta.