¿Cómo encajan las búsquedas en una interfaz RESTful?

114

Al diseñar una interfaz REST, la semántica de los tipos de solicitud se considera vital para el diseño.

  • GET : enumera la colección o recupera el elemento
  • PUT - Reemplazar colección o elemento
  • POST : crea una colección o elemento
  • ELIMINAR : bueno, erm, eliminar colección o elemento

Sin embargo, esto no parece cubrir el concepto de "búsqueda".

Por ejemplo, al diseñar un conjunto de servicios web que admiten un sitio de búsqueda de empleo, es posible que tenga los siguientes requisitos:

  • obtener anuncio de trabajo individual
    • GET a domain/Job/{id}/
  • Crear anuncio de trabajo
    • POST a domain/Job/
  • Actualizar anuncio de trabajo
    • PONER a domain/Job/
  • Eliminar anuncio de trabajo
    • ELIMINAR a domain/Job/

"Obtener todos los trabajos" también es simple:

  • GET a domain/Jobs/

Sin embargo, ¿cómo encaja la búsqueda de trabajo en esta estructura?

Usted podría afirmar que es una variación de "colección de listas" e implementarlo como:

  • GET a domain/Jobs/

Sin embargo, las búsquedas pueden ser complejas y es completamente posible generar una búsqueda que genere una cadena GET larga. Es decir, haciendo referencia a una pregunta de SO aquí , hay problemas con el uso de cadenas GET de más de 2000 caracteres.

Un ejemplo podría estar en una búsqueda facetada, continuando con el ejemplo de "trabajo".

Puedo permitir la búsqueda de facetas: "Tecnología", "Título del trabajo", "Disciplina", así como palabras clave de texto libre, edad del trabajo, ubicación y salario.

Con una interfaz de usuario fluida y una gran cantidad de tecnologías y títulos de trabajo, es posible que una búsqueda abarque una gran cantidad de opciones de facetas.

Ajuste este ejemplo en CVs, en lugar de trabajos, traiga aún más facetas, y puede fácilmente imaginar una búsqueda con cientos de facetas seleccionadas, o incluso 40 facetas cada una de 50 caracteres (por ejemplo, títulos de trabajo, Nombres de universidades, nombres de empleadores).

En esa situación, podría ser conveniente mover un PUT o POST para garantizar que los datos de búsqueda se enviarán correctamente. Por ejemplo:

  • POST a domain/Jobs/

Pero semánticamente es una instrucción para crear una colección.

Usted podría también dice que expresará esto como la creación de una búsqueda:

  • POST a domain/Jobs/Search/

o (según lo sugerido por burninggramma a continuación)

  • POST a domain/JobSearch/

Semánticamente puede parecer que tiene sentido, pero en realidad no estás creando nada, estás solicitando datos.

Por lo tanto, semánticamente es un GET , pero GET no garantiza que sea compatible con lo que necesitas.

Entonces, la pregunta es: intentar mantener lo más fiel al diseño RESTful posible, y al mismo tiempo garantizar que me mantengo dentro de las limitaciones de HTTP, ¿cuál es el diseño más apropiado para una búsqueda?

    
pregunta Rob Baillie 21.03.2014 - 11:59
fuente

7 respuestas

74

No debe olvidar que las solicitudes GET tienen algunas ventajas superiores sobre otras soluciones:

1) Las solicitudes GET se pueden copiar de la barra de URL, son digeridas por los motores de búsqueda, son "amigables". Donde "amigable" significa que normalmente una solicitud GET no debería modifica cualquier cosa dentro de tu aplicación (idempotent) . Este es el caso estándar para una búsqueda.

2) Todos estos conceptos son muy importantes no solo desde el usuario y el motor de búsqueda, sino desde el punto de vista arquitectónico, diseño de API .

3) Si crea una solución con POST / PUT , tendrá problemas en los que no está pensando en este momento. Por ejemplo, en el caso de un navegador, el botón de navegar hacia atrás / actualizar página / historial. Estos pueden resolverse, por supuesto, pero esa será otra solución, luego otra y otra ...

Considerando todo esto, mi consejo sería:

a) Debería poder encajar dentro de su GET with utilizando estructura de parámetros inteligente . En el caso extremo, incluso puede optar por tácticas como esta búsqueda de google donde establecí muchos parámetros, todavía es una url súper corta.

b) Cree otra entidad en su aplicación como JobSearch . Suponiendo que tiene tantas opciones, es probable que necesite almacenar estas búsquedas y administrarlas, por lo que simplemente está aclarando su aplicación. Puede trabajar con los objetos JobSearch como una entidad completa, lo que significa que puede probarlo / usarlo más fácilmente .

Personalmente intentaría pelear con todas mis garras para hacerlo con a) y cuando se pierda toda esperanza, me arrastraría hacia atrás con lágrimas en los ojos a la opción b) .

    
respondido por el p1100i 21.03.2014 - 13:44
fuente
10

En REST, la definición de recurso es muy amplia. Realmente es como quieras agrupar algunos datos.

  • Es útil pensar en un recurso de búsqueda como un recurso de recopilación. Los parámetros de consulta, a veces llamados la parte de búsqueda del URI, reducen el recurso a los elementos en los que está interesado el cliente.

Por ejemplo, el URI de Google principal apunta a un recurso de recopilación de "enlaces a todos los sitios en Internet". Los parámetros de consulta lo reducen a los sitios que desea ver.

(URI = identificador universal de recursos, de los cuales URL = localizador universal de recursos, donde el conocido "http: //" es el formato predeterminado para un URI. Por lo tanto, URL es un localizador, pero en REST es bueno generalizarlo para un identificador de recursos. Sin embargo, las personas los usan de manera intercambiable.)

  • Dado que el recurso que está buscando en su ejemplo es la colección de trabajos, tiene sentido buscar con
  

¿GET site / jobs? type = blah & location = here & etc = etc

     

(volver) {trabajos: [{trabajo: ...}]}

Y luego use POST, que es el verbo adjuntar o procesar para agregar nuevos elementos a esa colección:

  

Sitio / trabajos POST

     

{trabajo: ...}

  • Tenga en cuenta que es la misma estructura para el objeto job en cada caso. Un cliente puede OBTENER una colección de trabajos, usando parámetros de consulta para limitar la búsqueda, y luego usar el mismo formato para uno de los elementos para POSTAR un nuevo trabajo. O puede llevar uno de esos elementos y PONERlo a su URI para actualizarlo.

  • Para cadenas de consulta realmente largas o complicadas, la convención hace que sea correcto enviarlas como solicitudes POST. Agrupe los parámetros de consulta como pares de nombre / valor u objetos anidados en una estructura JSON o XML y envíelos en el cuerpo de la solicitud. Por ejemplo, si su consulta tiene datos anidados en lugar de un grupo de pares de nombre / valor. La especificación HTTP para POST lo describe como el verbo de anexar o procesar. (Si desea navegar un barco de guerra a través de una laguna en REST, use POST.)

Sin embargo, lo usaría como plan alternativo.

Lo que pierdes cuando haces eso es a) GET es nullipotente, es decir, no cambia nada, POST no lo es. Entonces, si la llamada falla, el middleware no volverá a intentar automáticamente o almacenará en caché los resultados, y 2) con los parámetros de búsqueda en el cuerpo, ya no podrá cortar y pegar el URI. Es decir, el URI no es un identificador específico para la búsqueda que desea.

Para diferenciar entre "crear" de "búsqueda". Hay un par de opciones que son consistentes con la práctica REST:

  • Puede hacerlo en el URI agregando algo al nombre de la colección, como búsqueda de trabajo en lugar de trabajos. Eso solo significa que está tratando la colección de búsqueda como un recurso separado.

  • Dado que la semántica de POST es tanto el proceso OR añadido, podría identificar los cuerpos de búsqueda con la carga útil. Me gusta {trabajo: ...} vs. {búsqueda: ...}. Depende de la lógica POST publicar o procesarla adecuadamente.

Eso es más o menos una preferencia de diseño / implementación. No creo que haya una convención clara.

Por lo tanto, como ya has establecido, la idea es definir un recurso de recopilación para jobs

  

sitio / trabajos

Busque con GET + parámetros de consulta para limitar la búsqueda. Las consultas de datos largas o estructuradas entran en el cuerpo de un POST (posiblemente a una colección de búsqueda separada). Crea con POST para anexar a la colección. Y actualice con PUT a un URI específico.

(FWIW, la convención de estilo con URI es usar todo en minúsculas con palabras separadas por guiones. Pero eso no significa que tengas que hacerlo de esa manera.)

(Además, debo decir que de tu pregunta, está claro que estás muy lejos en este camino. Deletreé las cosas de forma explícita solo para alinearlas, pero tu pregunta ya había abordado la mayor parte de las preguntas semánticas). Problemas en esta respuesta. Simplemente lo estaba vinculando con algunas convenciones y prácticas.

    
respondido por el Rob 21.03.2014 - 15:52
fuente
8

En general, utilizo consultas OData, funcionan como una llamada GET, pero te permiten restringir las propiedades que se devuelven y filtrarlas.

Usas tokens como $select= y $filter= , por lo que terminarás con un URI que se parece a esto:

/users?$select=Id,Name$filter=endswith(Name, 'Smith')

También puedes hacer la paginación usando $skip y $top y ordenando.

Para obtener más información, consulte OData.org . No ha especificado qué idioma está utilizando, pero si se trata de ASP.NET, la plataforma WebApi admite consultas OData. Para otros (PHP, etc.) probablemente haya bibliotecas que puede usar para traducirlas en consultas de base de datos.

    
respondido por el Trevor Pilley 21.03.2014 - 13:17
fuente
8

TL; DR: GET para el filtrado, POST para la búsqueda

Hago una distinción entre filtrar los resultados de listar una colección frente a una búsqueda compleja. La prueba de fuego que uso es básicamente si necesito más que filtrar (positivo, negativo o rango) considero que es una búsqueda más compleja que requiere POST.

Esto tiende a reforzarse cuando se piensa en lo que se devolverá. Por lo general, solo uso GET si un recurso tiene un ciclo de vida casi completo (PUT, DELETE, GET, collection GET) . Normalmente, en una colección GET devuelvo una lista de URI que son los recursos REST que conforman esa colección. En una consulta compleja, es posible que extraiga varios recursos para crear la respuesta (piense en SQL join) , por lo que no enviaré URI, sino datos reales. El problema es que los datos no se representarán en un recurso, por lo que siempre tendré que devolver los datos. Esto me parece un caso claro de requerir un POST.

    
respondido por el dietbuddha 24.03.2014 - 08:10
fuente
5

Un enfoque a considerar es tratar el conjunto de posibles consultas como un recurso de recopilación, por ejemplo. /jobs/filters .

POST solicita este recurso, con los parámetros de consulta en el cuerpo, creará un nuevo recurso o identificará un filtro equivalente existente y devolverá una URL con su ID: /jobs/filters/12345 .

La identificación se puede usar en una solicitud GET para trabajos: /jobs?filter=12345 . Las solicitudes de GET posteriores en el recurso de filtro devolverán la definición del filtro.

Este enfoque tiene la ventaja de que lo libera del formato de parámetro de consulta para la definición de filtro, lo que posiblemente le proporcione más poder para definir filtros complejos. Las condiciones O son un ejemplo en el que puedo pensar que son difíciles de cumplir con cadenas de consulta.

Un inconveniente de este enfoque es que pierde la legibilidad de la URL (aunque esto puede mitigarse al recuperar la definición a través de una solicitud GET para el recurso de filtro). Por este motivo, es posible que también desee admitir el mismo o un subconjunto de los parámetros de consulta en el recurso /jobs como lo haría para un recurso de filtro. Esto podría ser utilizado para consultas más cortas. Si se proporciona esta función, para mantener el almacenamiento en caché entre los dos tipos de filtrado, cuando se usan parámetros de consulta en el recurso /jobs , la implementación debe crear / reutilizar internamente un recurso de filtro y devolver un estado 302 o 303 indicando la URL en la forma de /jobs?filter=12345 .

    
respondido por el pgraham 27.03.2014 - 15:30
fuente
5

Esta es una respuesta antigua, pero aún puedo contribuir un poco a la discusión. He observado muy a menudo un malentendido de REST, RESTful y Architecture. RESTful nunca menciona nada sobre NO construir búsqueda, no hay nada en RESTful sobre arquitectura, es un conjunto de principios o criterios de diseño.

Para describir una búsqueda de una mejor manera, tenemos que hablar de una arquitectura en particular y la que mejor se adapta a las necesidades es la arquitectura orientada a recursos (ROA).

En RESTful hay principios para diseñar, idempotent no significa que el resultado no pueda cambiar mientras leo algunas respuestas, significa que el resultado de una solicitud independiente no depende de cuántas veces se ejecute. Puede cambiar, imaginemos que estoy actualizando continuamente una base de datos que la alimenta con algunos datos proporcionados por una API RESTful, la ejecución del mismo GET puede cambiar el resultado, pero no depende de cuántas veces se haya ejecutado. Si puedo congelar el mundo, significa que no hay estado, transformación, nada dentro del servicio cuando solicito el recurso que conduce a un resultado diferente.

  

Por definición, un recurso es algo que es importante ser   referenciado como una cosa por sí mismo.

En una arquitectura orientada a recursos (llamémosla ROA de ahora en adelante por brevedad) nos enfocamos en el recurso que podría ser muchas cosas:

  • Una versión de un documento
  • La última versión actualizada del documento
  • Un resultado que proviene de una búsqueda
  • Una lista de objetos
  • El primer artículo que compré en un comercio electrónico

Lo que lo hace único en términos de recursos es la direccionabilidad , lo que significa que tiene solo un URI

De esa manera, la búsqueda encaja perfectamente en REST considerando la ROA . Tenemos que usar GET porque asumo que su búsqueda es una búsqueda normal y no cambia nada, por lo que es idempotente (incluso si devuelve cosas diferentes según los nuevos elementos agregados). Hay una confusión aquí de esa manera porque podría apegarme a RESTful y no a ROA, significa que podría seguir un patrón que crea una búsqueda y devolver diferentes cosas con los mismos parámetros porque no estoy usando el principio de direccionabilidad de ROA. ¿Como es eso? Bueno, si envía los filtros de búsqueda en el cuerpo o encabezado, el recurso no es DIRIGABLE.

Puede encontrar los principios de lo que es exactamente y la URI en el documento original de W3:

enlace

Cualquier URL en esta arquitectura debe ser autodescriptiva. Es necesario si sigue los principios para abordar todo en el URI, esto significa que puede usar / (barra) para separar lo que necesite o consultar los parámetros. Sabemos que existen limitaciones, pero este es el patrón de la arquitectura.

Siguiendo el patrón ROA en REST. una búsqueda no es más que cualquier otro recurso, la única diferencia es que los recursos provienen de un cálculo en lugar de una relación directa con el objeto en sí. Según el principio, podría abordar y obtener un servicio de cálculo aritmético simple basado en el siguiente patrón:

enlace

Donde suma, 1 y 2 se pueden modificar pero el resultado del cálculo es único y se puede direccionar, cada vez que llamo con los mismos parámetros obtengo lo mismo y nada cambia en el servicio. El recurso / sum / 1/2 y / substract / 5/4 se adhieren perfectamente a los principios.

    
respondido por el Maximiliano Rios 08.03.2017 - 06:18
fuente
3

GET está bien, si tiene una colección estática que siempre devuelve los mismos resultados (representación) para un URI. Eso también implica que los datos que generan estas representaciones nunca se alteran. La fuente es una base de datos de solo lectura.

El hecho de que GET devuelva resultados diferentes para uno y el mismo URI infringe idempotency / safety y CoolURI Principio y, en consecuencia, no RESTful . Es posible tener verbos idempotentes escritos en una base de datos, pero nunca deben afectar la representación.

Una búsqueda común comienza con una solicitud POST que devuelve una referencia al resultado. Genera el resultado (es nuevo y se puede recuperar con un GET posterior). Este resultado puede ser jerárquico (referencias adicionales con URI que puede OBTENER), por supuesto, y podría reutilizar elementos de búsquedas anteriores, si tiene sentido para la aplicación.

Por cierto, sé que la gente lo hace de manera diferente. No necesita explicarme lo conveniente que puede ser violar REST.

    
respondido por el Martin Sugioarto 11.09.2016 - 02:19
fuente

Lea otras preguntas en las etiquetas