¿Cuál es la forma correcta de hacer un método de búsqueda RESTful complejo?

44

Siguiendo los principios REST, me gustaría crear un método GET para mi API que realice una búsqueda utilizando algunos criterios y devuelva los resultados al cliente. El problema es que los criterios pueden tener hasta 14 parámetros, uno de ellos es una lista de objetos complejos, así que ...

  • Ni siquiera sé si es posible codificar / decodificar estos objetos complejos a / desde parámetros de URL.

  • No calculé cuánto tiempo podría tardar la url, pero estoy seguro de que será lo suficientemente grande y tal vez alcance el límite de longitud de la url.

Además, la búsqueda debe mostrar los resultados en "tiempo real", es decir, cada vez que el usuario cambia algo del formulario de búsqueda, debería poder ver los nuevos resultados sin presionar ningún botón de "búsqueda".

¿Podría aclararme estos puntos y cuál sería su consejo para crear un método de búsqueda tranquilo con muchos parámetros?

anat0lius
fuente
3
Como comentario aparte (observo que ninguna de las dos respuestas en el momento de la redacción menciona la parte en tiempo real), las búsquedas en tiempo real normalmente solo deberían enviar la solicitud actualizada una y otra vez. Por ejemplo, mientras se está escribiendo, que estaría enviando solicitudes para cosas como search?q=t, search?q=te, search?q=test, y así sucesivamente. Considere limitar la frecuencia con la que se envía la consulta para evitar dañar su servidor. Alternativamente, también puede devolver una gran cantidad de información y del lado del cliente hacer el filtrado. Eso funciona bien si el usuario ingresa en categorías amplias que pueden reducir mucho las cosas.
Kat
2
No importa la solución, asegúrese de intercambiar datos en un formato neutral de tecnología. Por lo tanto, no use una representación interna o será más difícil escribir clientes para su API.
Kwebble
Dependiendo de sus necesidades, esta biblioteca podría hacer el trabajo: github.com/jirutka/rsql-parser . Tiene una extensión JPA si usa JPA, o puede escribir su propio visitante para asignar a la API de su motor de búsqueda. Y puede agregar su propio operador.
Walfrat

Respuestas:

54

Antes de leer mi respuesta, me gustaría decir que estuve de acuerdo con @Neil. Tenemos que elegir nuestras batallas. Por lo general, queremos hacerlo lo mejor posible, pero a veces hay muy poco espacio para la discusión y tenemos que tomar decisiones en contra de nuestra voluntad.

De todos modos, en la respuesta de Neil, extraño una cosa más. Documentación . Solo para garantizar que los desarrolladores sepan que las solicitudes POST /searchson seguras.

Eso dicho

1. Dale la oportunidad de OBTENER

Considere la GETopción primero. Echa un vistazo a la longitud máxima de la URL de esta pregunta . Evalúe si su cadena de consulta más larga tiene más de 2000 caracteres. Si no es así, y no esperas que sea así, ve con GET. Puede parecer feo, pero al menos puede marcar la URL y, por supuesto, tiene todas las ventajas derivadas de la semántica del método (idempotencia, seguridad y almacenamiento en caché)

1.1 Intente codificar la cadena de consulta

Por ejemplo, en base 64. Incluso JavaScript admite codificaciones de base 64 .

Así es como funciona:

  1. Construya el JSON con todos los filtros y normalícelo.
  2. Analizarlo en cadena
  3. Codificarlo
  4. Envíe el JSON codificado como request param ( /search?q=SGVsbG8gV29ybGQh....).
  5. En el lado del servidor, decodifique el parámetro q .
  6. Deserializar la cadena JSON

Anteriormente, haga la cadena JSON más larga posible, codifíquela y tome la longitud. Evaluar si la cadena codificada encaja en la URL. He implementado el siguiente fragmento en Fiddle.js para que lo pruebes . (Espero que todavía funcione) 1

Las codificaciones de base 64 son deterministas y reversibles, por lo que no hay posibilidad de colisiones.

Con consultas codificadas, también podríamos guardar búsquedas en la base de datos, marcar la URL, compartir enlaces, etc. Y, por supuesto, no tenemos que escapar / escapar de la cadena.

1.2 Probar con alias

Al leer este blog sobre cómo diseñar API REST, recordé una alternativa más. Alias ​​para consultas comunes .

Me parecen interesantes por las siguientes razones.

  • Acorte la longitud de la cadena de consulta. Hace que la API sea más limpia y fácil de usar

    GET / tickets /? Status = cerrado y cerrado At = xxx vs GET / tickets / recientemente cerrado /

  • Combinable con más alias o más parámetros de solicitud.

    GET / tickets /? Status = cerrado y cerradoAt = xxx & dentro = 30min vs GET / tickets / recientemente cerrado /? Dentro = 30min

  • Podemos combinar alias con cadenas de consulta codificadas

    GET / tickets /? Status = cerrado y cerradoAt = xxx & dentro = 30min vs GET / tickets / recientemente cerrado /? Q = SGVsbG8g ...


1: He usado JSON, pero podríamos usar otros formatos tan pronto como podamos deserializarlo en el lado del servidor.

Laiv
fuente
2
Esto es práctico y correcto. También vale la pena señalar que la mayoría de los lenguajes de programación hacen que sea trivial transformar un hash en una cadena de consulta, por lo que comenzar con una acción GET es muy fácil de hacer.
Aluan Haddad
1
Me encanta Spring stackoverflow.com/questions/16942193/… No puedo creer que funcionó en el primer intento: D. Sobre la longitud de la url, es inferior a 1k, aunque aún necesitamos iterar las especificaciones.
anat0lius
Entonces, ve con GET. Por simplicidad. Con Spring MVC, puede lograr el mismo mapeo con GET. Busque SpringArgumentResolver de Spring ;-)
Laiv
Base64 infla el tamaño de la carga útil en aproximadamente 4/3. Si bien la codificación urinaria puede llegar a 3/1 para caracteres especiales, las consultas con caracteres mayormente seguros mantendrán el mismo tamaño. ¿Hay alguna otra razón para usar base64?
villasv
Realmente no. Simplemente no me gusta (des) las URL de escape. El sobrepeso de la carga útil es la compensación aquí. Todavía tiene que caber dentro del tamaño máximo de GET por solicitud. Por eso construí el fragmento. Para que el usuario lo intente. Cuando escribí la respuesta, prioricé la semántica web sobre los detalles de implementación. El punto de la respuesta es "sigue intentándolo con GET". Encuentra tu camino o utiliza cualquiera de estos que comparto contigo.
Laiv
13

Si todo lo que tienes es un martillo, todo parece un clavo. Parece que el problema aquí es que está tratando de convertir una página de búsqueda en RESTful, y esto difícilmente parece ser un patrón común para el diseño RESTful para resolver.

Simplemente vaya con una solicitud POST con los parámetros proporcionados por el usuario para obtener la información que necesita del backend. Supongo que no necesita hacer nada más que realizar una búsqueda, por lo que no hay posibilidad de que tenga que insertar a través de esta página. Simplemente agregue una / búsqueda al final de su URL para no correr el riesgo de tener conflictos con su página / usuarios que sería RESTful.

Neil
fuente
2
@LiLou_: Para ese requisito, solo hay dos posibilidades realistas: 1. Lea todos los datos en su front-end y realice el filtrado allí. Esto puede ser prohibitivo en la cantidad de memoria requerida. 2. Haga una nueva solicitud al servidor para cada cambio en los criterios de búsqueda. No importa si se trata de una solicitud POST o GET, pero la latencia de red involucrada puede ser perjudicial para el sentido del usuario de actualizaciones "en tiempo real".
Bart van Ingen Schenau
2
Estoy de acuerdo en estar en desacuerdo, POST significa algo más semánticamente. Sugeriría ir con el GET y pasar todos los datos del filtro en el parámetro de consulta ahora si hay demasiados parámetros, entonces es un error en el nivel de la aplicación.
CodeYogi
2
@CodeYogi a veces el cliente no le da espacio para la discusión. He implementado páginas de vista tipo Excel con 40-50 columnas cada una con su propio filtro. Y ordenable por supuesto. De todos modos, todavía es posible con GET, pero puede que no parezca demasiado moda
Laiv
1
@Laiv en ese caso, debe haber una discusión seria porque POST está destinado a cambiar el estado del servidor. Los casos de uso como este no son excepcionales, por lo tanto, deben tratarse sin hacks.
CodeYogi
2
En estos casos, la documentación es imprescindible. He tenido una discusión seria con los clientes sobre la usabilidad de sus aplicaciones. Más tarde se demostró que tenía razón porque el usuario final estuvo de acuerdo conmigo. Sin embargo, a veces tienes que elegir tus batallas.
Laiv
0

Depende completamente de cuál sea su modelo de API: como ninguno o como verbo.

Si la API no es válida, entonces puede obtener una lista de objetos de la siguiente manera:

GET: /api/v1/objects

En este caso, debe enviar datos como parámetros de solicitud. Por lo tanto, debe describir sus parámetros como una lista plana de valores-clave:

GET: /api/v1/objects

key1 : val1
key2.key1 : val 21
key2.key2 : val 22
....

Algunas plataformas admiten resolución de parámetros personalizados (por ejemplo, Spring MVC), y puede convertir parámetros en un objeto.

Mostafa
fuente