API REST Mejor práctica: Cómo aceptar la lista de valores de parámetros como entrada

Estamos lanzando una nueva API REST y quería información de la comunidad sobre las mejores prácticas sobre cómo deberíamos tener los parámetros de entrada formateados:

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 API está centrada en JSON, hemos ido por un camino donde 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 está teniendo a mano la entrada de JSON? Más de un dolor?

Es posible que deseemos aceptar ambos estilos, pero ¿esa flexibilidad en realidad causa más confusión y dolores de cabeza (capacidad de mantenimiento, documentación, etc.)?

Un caso más complejo es cuando queremos ofrecer entradas más complejas. 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 juntas.

Al final, ¿esto realmente importa? Es probable que haya tantas utilidades JSON por ahí que el tipo de entrada simplemente no importe tanto.

Sé que nuestros clientes de JavaScript haciendo 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 un ID universal único. Demasiadas personas quedan atrapadas en la estructura de los URI y qué URI son más “tranquilos” que otros. Este argumento es tan ridículo como decir que nombrar a alguien “Bob” es mejor que nombrarlo “Joe”: ambos nombres hacen el trabajo de “identificar a una persona”. Un URI no es más que un nombre universal único .

Entonces, en los ojos de REST, discutir si ?id=["101404","7267261"] es más tranquilo que ?id=101404,7267261 o \Product\101404,7267261 es algo inútil.

Ahora, una vez dicho esto, muchas veces cómo se construyen los URI generalmente puede servir como un buen indicador para otros problemas en un servicio RESTful. Hay un par de banderas rojas en sus URI y preguntas en general.

Sugerencias

  1. Múltiples URI para el mismo recurso y Content-Location

    Es posible que deseemos aceptar ambos estilos, pero ¿esa flexibilidad en realidad causa más confusión y dolores de cabeza (capacidad de mantenimiento, documentación, etc.)?

    Los URI identifican recursos. Cada recurso debe tener un URI canónico. Esto no significa que no pueda hacer que dos URI apunten al mismo recurso, pero hay formas bien definidas de hacerlo. Si decide utilizar los formatos JSON y basados ​​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 apuntan al mismo “recurso” deben incluir el encabezado Content-Location .

    Siguiendo con la analogía del nombre, tener varios URI es como tener apodos para las personas. Es perfectamente aceptable y, a menudo, bastante útil, sin embargo, si estoy usando un apodo, probablemente quiera saber su nombre completo, la forma “oficial” de referirse a esa persona. De esta manera, cuando alguien menciona a alguien por su nombre completo, “Nichloas Telsa”, sé que están hablando de la misma persona a la que me refiero como “Nick”.

  2. “Buscar” en su URI

    Un caso más complejo es cuando queremos ofrecer entradas más complejas. Por ejemplo, si queremos permitir múltiples filtros en la búsqueda …

    Una regla general es que si tu URI contiene un verbo, puede ser una indicación de que algo está desactivado. Los URI identifican un recurso, sin embargo, no deberían indicar lo que le estamos haciendo a ese recurso. Ese es el trabajo de HTTP o en términos relajantes, nuestra “interfaz uniforme”.

    Para vencer al nombre analogy dead, usar un verbo en un URI es como cambiar el nombre de una persona cuando quieres interactuar con ellos. Si estoy interactuando con Bob, el nombre de Bob no se convierte en “BobHi” cuando quiero saludarlo. Del mismo modo, cuando queremos “buscar” Productos, nuestra estructura de URI no debe cambiar de “/ Producto / …” a “/ Buscar / …”.

Respondiendo a su pregunta inicial

  1. Con respecto a ["101404","7267261"] vs 101404,7267261 : Mi sugerencia aquí es evitar la syntax JSON por simplicidad (es decir, no requiera que sus usuarios hagan encoding URL cuando realmente no lo necesite). Hará que tu API sea un poco más útil. Mejor aún, como otros han recomendado, vaya con el formato estándar application/x-www-form-urlencoded , ya que probablemente sea más familiar para sus usuarios finales (por ejemplo ?id[]=101404&id[]=7267261 ). Puede que no sea “bonita”, pero URI bonitos no necesariamente significa URI utilizables. Sin embargo, para reiterar mi punto inicial, sin embargo, al hablar de REST, no importa. No pienses demasiado en eso.

  2. Su ejemplo de URI de búsqueda compleja puede resolverse de forma muy similar a su ejemplo de producto. Recomiendo volver a application/x-www-form-urlencoded formato application/x-www-form-urlencoded ya que ya es un estándar con el que muchos están familiarizados. Además, recomendaría unir los dos.

Tu URI …

 /Search?term=pumas&filters={"productType":["Clothing","Bags"],"color":["Black","Red"]} 

Su URI después de haber sido codificado por 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 a …

 /Product?term=pumas&productType[]=Clothing&productType[]=Bags&color[]=Black&color[]=Red 

Además de evitar el requisito de encoding 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 desea recuperar un Producto o Lista de Productos (ambos se consideran un único “recurso” en términos RESTful), están interesados ​​en /Product/... URIs.

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 de los códigos del servidor interpretarán esto como una lista de valores, aunque muchos tienen simplificaciones de un solo valor, por lo que es posible que tenga que buscarlos.

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 si no es en teoría).

La forma en que he visto que algunos hacen una consulta complicada RESTfully es en dos pasos:

  1. POST sus requisitos de consulta, recibiendo una ID (esencialmente creando un recurso de criterios de búsqueda)
  2. GET la búsqueda, haciendo referencia a la ID anterior
  3. opcionalmente ELIMINAR 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 formas

http://our.api.com/Product/ : si solo quieres un registro

http://our.api.com/Product : si quieres todos los registros

http://our.api.com/Product/, : como James sugirió 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 Hypermedia como el motor de estado de la aplicación (HATEOAS) propiedad de un RestFul WS y realizar una llamada http://our.api.com/Product que debe devolver las direcciones URL equivalentes de http://our.api.com/Product/ y llámalos después de esto.

Segundo

Cuando tiene que hacer consultas en las llamadas url. Sugeriría usar HATEOAS nuevamente.

1) Haz una llamada a http://our.api.com/term/pumas/productType/clothing/color/black

2) Haz una llamada a http://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 urls con todas las posibles urls de ropa -> llame a las que quiere (ropa y bolsos) – > recibe las posibles URL de color -> llama las que quieras

Es posible que desee comprobar RFC 6570 . Esta plantilla de URI muestra muchos ejemplos de cómo las URL pueden contener parámetros.

Primer caso:

Una búsqueda de producto normal se vería así

http://our.api.com/product/1

Así que estoy pensando que la mejor práctica sería que hagas esto

http://our.api.com/Product/101404,7267261

Segundo caso

Buscar con los parámetros querystring – bien así. 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 haga sentir cómodo.

La razón para colocar los datos en la url es para que el enlace pueda pegarse en un sitio / compartido entre los usuarios. Si esto no es un problema, use un JSON / POST en su lugar.

EDITAR: reflexionando, creo que este enfoque se adapta a una entidad con una clave compuesta, pero no a una consulta para múltiples entidades.

Estaré de acuerdo con la respuesta de nategood, ya que está completa y parece haber satisfecho sus necesidades. Sin embargo, me gustaría añadir un comentario sobre la identificación de múltiples (1 o más) recursos de esa manera:

http://our.api.com/Product/101404,7267261

Al hacerlo, usted:

Complementa los clientes obligándolos a interpretar tu 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 el anterior para obtener 1 o muchos. Dado que no debe mostrar más de 1 página de detalles a un usuario por el bien de UX, creo que tener más de 1 ID sería inútil y se usaría puramente para filtrar los productos.

Puede que no sea tan problemático, pero tendrá que manejarlo usted mismo al devolver una sola entidad (verificando si su respuesta contiene uno o más) o dejar que los clientes lo administren.

Ejemplo

Quiero pedir un libro de Amazing . Sé exactamente qué libro es y lo veo en el listado cuando navego por libros de terror:

  1. 10 000 líneas increíbles, 0 prueba increíble
  2. El regreso del monstruo asombroso
  3. Vamos a duplicar el código increíble
  4. El sorprendente comienzo del fin

Después de seleccionar el segundo libro, me redirigen a una página que detalla el libro como parte de una lista:

 -------------------------------------------- Book #1 -------------------------------------------- Title: The return of the amazing monster Summary: Pages: Publisher: -------------------------------------------- 

¿O en una página que me da los detalles completos de ese libro solamente?

 --------------------------------- The return of the amazing monster --------------------------------- Summary: Pages: Publisher: --------------------------------- 

Mi opinión

Sugiero usar la ID en la variable de ruta cuando se garantiza la unicidad cuando se obtienen los detalles de este recurso. Por ejemplo, las API a continuación sugieren varias maneras de obtener los detalles de un recurso específico (suponiendo que un producto tiene un ID único y una especificación para ese producto tiene un nombre único y puede navegar de arriba hacia abajo):

 /products/{id} /products/{id}/specs/{name} 

En el momento en que necesite más de 1 recurso, le sugiero que filtre desde una colección más grande. Para el mismo ejemplo:

 /products?ids= 

Por supuesto, esta es mi opinión, ya que no se impone.