Al diseñar una interfaz RESTful, la semántica de los tipos de solicitud se considera vital para el diseño.
- GET - Lista de colección o elemento de recuperación
- PUT - Reemplazar colección o elemento
- POST - Crear colección o elemento
- BORRAR - 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
- Llegar a
domain/Job/{id}/
- Llegar a
- Crear anuncio de trabajo
- Publicar en
domain/Job/
- Publicar en
- Actualizar anuncio de trabajo
- PONER a
domain/Job/
- PONER a
- Eliminar anuncio de trabajo
- BORRAR a
domain/Job/
- BORRAR a
"Obtener todos los trabajos" también es simple:
- Llegar a
domain/Jobs/
Sin embargo, ¿cómo cae el trabajo "búsqueda" en esta estructura?
Usted podría reclamar que es una variación de "colección de listas" y poner en práctica como:
- Llegar a
domain/Jobs/
Sin embargo, las búsquedas pueden ser complejas y es completamente posible producir una búsqueda que genere una cadena GET larga. Es decir, haciendo referencia a una pregunta SO aquí , hay problemas al usar cadenas GET de más de aproximadamente 2000 caracteres.
Un ejemplo podría estar en una búsqueda facetada, continuando con el ejemplo de "trabajo".
Puedo permitir la búsqueda en 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 factible que una búsqueda abarque una gran cantidad de opciones de facetas.
Ajusta este ejemplo a CV, en lugar de trabajos, trae aún más facetas, y puedes imaginar fácilmente una búsqueda con un centenar de facetas seleccionadas, o incluso solo 40 facetas, cada una de las cuales tiene 50 caracteres (por ejemplo, títulos de trabajo, nombres de universidades, Nombres del empleador).
En esa situación, puede ser conveniente mover un PUT o POST para garantizar que los datos de búsqueda se envíen correctamente. P.ej:
- Publicar en
domain/Jobs/
Pero semánticamente esa es una instrucción para crear una colección.
También podría decir que expresará esto como la creación de una búsqueda:
- Publicar en
domain/Jobs/Search/
o (como lo sugiere el burninggramma a continuación)
- Publicar en
domain/JobSearch/
Semánticamente parece tener sentido, pero en realidad no estás creando nada, estás haciendo una solicitud de datos.
Entonces, semánticamente es un GET , pero no se garantiza que GET respalde lo que necesita.
Entonces, la pregunta es: tratando de mantenerme tan fiel al diseño RESTful como sea posible, mientras me aseguro de mantenerme dentro de las limitaciones de HTTP, ¿cuál es el diseño más apropiado para una búsqueda?
fuente
domain/Jobs?keyword={keyword}
. Esto funciona bien para mí :) Mi esperanza es que elSEARCH
verbo se convierta en un estándar. programmers.stackexchange.com/questions/233158/…Respuestas:
No debe olvidar que las solicitudes GET tienen algunas ventajas superiores sobre otras soluciones:
1) Las solicitudes GET pueden copiarse desde la barra de URL, son digeridas por los motores de búsqueda, son "amigables". Donde "amigable" significa que normalmente una solicitud GET no debe modificar nada dentro de su aplicación (idempotente) . 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 un punto de vista arquitectónico y de diseño de API .
3) Si crea una solución alternativa 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 navegación hacia atrás / actualizar página / historial. Esto se puede resolver, por supuesto, pero será otra solución, luego otra y otra ...
Considerando todo esto, mi consejo sería:
a) Debería poder adaptarse a su GET utilizando una estructura de parámetros inteligente . En el caso extremo, incluso puede optar por tácticas como esta búsqueda de Google, donde configuré muchos parámetros, pero es una URL muy corta.
b) Cree otra entidad en su aplicación como JobSearch . Suponiendo que tenga tantas opciones, es probable que también necesite almacenar estas búsquedas y administrarlas, por lo que solo está limpiando 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, trataría de luchar con todas mis garras para lograrlo con a) y cuando se pierda toda esperanza, me arrastraría de regreso con lágrimas en los ojos a la opción b) .
fuente
domain/Jobs/Search/
, tal vez con en sudomain/JobsSearch/
lugar, o quiso decir algo diferente? ¿Puedes aclarar?TL; DR: OBTENER para filtrar, POST para buscar
Hago una distinción entre filtrar los resultados de enumerar una colección frente a una búsqueda compleja. La prueba de tornasol que uso es básicamente si necesito más que filtrado (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 mayormente completo (PUT, DELETE, GET, collection GET) . Por lo general, en una colección GET, estoy devolviendo una lista de URI que son los recursos REST que conforman esa colección. En una consulta compleja, es posible que extraiga de varios recursos para construir la respuesta (piense en la combinación SQL) para que no envíe URI de regreso, sino datos reales. El problema es que los datos no estarán representados en un recurso, por lo que siempre tendré que devolver los datos. Esto me parece un caso claro de requerir una POST.
-
Ha pasado un tiempo y mi publicación original fue un poco descuidada, así que pensé en actualizar.
GET es la opción intuitiva para devolver la mayoría de los tipos de datos, colecciones de recursos REST, datos estructurados de un recurso, incluso cargas útiles singulares (imágenes, documentos, etc.).
POST es el método general para todo lo que no parece encajar en GET, PUT, DELETE, etc.
En este punto, creo que las búsquedas simples, el filtrado tienen sentido intuitivamente a través de GET. Las búsquedas complejas dependen de la preferencia personal, especialmente si está agregando funciones de agregación, correlaciones cruzadas (uniones), reformateadores, etc. Diría que los parámetros GET no deberían ser demasiado largos, y es una consulta grande (sin embargo, se está estructurando ) a menudo puede tener más sentido como un cuerpo de solicitud POST.
También considero el aspecto de experiencia del uso de API. En general, quiero que la mayoría de los métodos sean lo más fáciles de usar e intuitivos posible. Insertaré llamadas que son más flexibles (y, por lo tanto, más complejas) en POST y en un URI de recursos diferente, especialmente si es inconsistente con el comportamiento de otros recursos REST en la misma API.
De cualquier manera, la consistencia es probablemente más importante que si está haciendo una búsqueda en GET o POST.
Espero que esto ayude.
fuente
GET /class?queryParams
. Desde la perspectiva de un usuario, la "clase" siempre fue una cosa y no tenía que hacer ninguna unión SQL extraña.En REST, la definición de recursos es muy amplia. Realmente, sin embargo, desea agrupar algunos datos.
Por ejemplo, el URI principal de Google apunta a un recurso de recopilación de "enlaces a cada sitio en Internet". Los parámetros de consulta reducen eso 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 generalizar eso a un identificador de recurso Sin embargo, las personas los usan de manera intercambiable).
Y luego use POST, que es el verbo agregar o procesar para agregar nuevos elementos a esa colección:
Tenga en cuenta que es la misma estructura para el
job
objeto en cada caso. Un cliente puede OBTENER una colección de trabajos, utilizando parámetros de consulta para limitar la búsqueda, y luego usar el mismo formato para uno de los elementos para PUBLICAR un nuevo trabajo. O puede tomar uno de esos elementos y PONERLO en 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 la consulta como pares de nombre / valor, u objetos anidados en una estructura JSON o XML y envíelos al 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 anexar o procesar. (Si quieres navegar en un acorazado a través de una escapatoria en REST, usa POST).
Sin embargo, usaría eso como el plan alternativo.
Sin embargo, lo que pierde cuando hace eso es a) GET es nulipotente, es decir, no cambia nada, POST no lo es. Entonces, si la llamada falla, el middleware no volverá a intentar automáticamente ni guardará en caché los resultados, y 2) con los parámetros de búsqueda en el cuerpo, ya no podrá cortar ni pegar el URI. Es decir, el URI no es un identificador específico para la búsqueda que desea.
Para diferenciar entre "crear" de "buscar". 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 un proceso de agregar o OR, puede identificar cuerpos de búsqueda con la carga útil. Como {job: ...} vs. {search: ...}. Depende de la lógica POST publicarlo o procesarlo adecuadamente.
Eso es más o menos una preferencia de diseño / implementación. No creo que haya una convención clara.
Entonces, como ya ha establecido, la idea es definir un recurso de recopilación para
jobs
Busque con parámetros de consulta GET + para acotar la búsqueda. Las consultas de datos largas o estructuradas van al cuerpo de una POST (posiblemente a una colección de búsqueda separada). Cree con POST para agregar 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 tenga que hacerlo de esa manera).
(Además, debería decir que a partir de su pregunta, está claro que está muy lejos en este camino. Expliqué las cosas de manera explícita solo para alinearlas, pero su pregunta ya había abordado la mayoría de los problemas semánticos en este respuesta. Solo estaba entrelazándola con un poco de convención y práctica.)
fuente
route
realmente no se puede manejar la elección del procesamiento. Tendría que echarle un vistazo a eso ...Generalmente uso consultas OData, funcionan como una llamada GET pero le permiten restringir las propiedades que se devuelven y filtrarlas.
Utiliza tokens como
$select=
y,$filter=
por lo tanto, terminará con un URI que se parece a esto:También puede hacer paginación usando
$skip
y$top
y ordenando.Para obtener más información, visite OData.org . No ha especificado qué idioma está usando, pero si es ASP.NET, la plataforma WebApi admite consultas OData; para otros (PHP, etc.) probablemente haya bibliotecas que puede usar para traducirlas en consultas de bases de datos.
fuente
JobsNearMeAddedInTheLast7Days
o lo que sea para encapsular la consulta que es demasiado larga / compleja para OData y luego exponerla solo a través de llamadas GET .Un enfoque a considerar es tratar el conjunto de consultas posibles como un recurso de recopilación, por ejemplo
/jobs/filters
.POST
peticiones a este recurso, con los parámetros de consulta en el cuerpo, o bien se cree un nuevo recurso o identificar un filtro equivalente existente y devolver una dirección URL que contiene su ID:/jobs/filters/12345
.El ID puede ser utilizado en una petición GET de puestos de trabajo:
/jobs?filter=12345
. LasGET
solicitudes 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 del filtro, lo que le brinda más poder para definir filtros complejos. O las condiciones son un ejemplo que puedo pensar que son difíciles de lograr con las 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
GET
solicitud del recurso de filtro). Por esta razón, es posible que también desee admitir el mismo o un subconjunto de los parámetros de consulta en el/jobs
recurso como lo haría con un recurso de filtro. Esto podría usarse para consultas más cortas. Si se proporciona esta característica, para mantener la capacidad de almacenamiento en caché entre los dos tipos de filtrado, cuando se utilizan parámetros de consulta en el/jobs
recurso, la implementación debe crear / reutilizar internamente un recurso de filtro y devolver un estado302
o303
indicando la URL en forma de/jobs?filter=12345
.fuente
GET /jobs/37
y recibir un resultado, luego alguien elimina el recurso y 2 segundos después la misma solicitud devuelve un 404. De manera similar, si ustedPOST /searches
y usted son redirigidos a un resultado de búsqueda (la búsqueda se crea y recibe un 201 con Ubicación del encabezado del recurso), 2 segundos después, ese resultado puede borrarse de la memoria y debe regenerarse. No hay necesidad de almacenamiento a largo plazo.Esta es una vieja respuesta, pero aún puedo contribuir un poco a la discusión. He observado muy a menudo un malentendido de REST, RESTful y Arquitectura. 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 sobre una arquitectura en particular y la que mejor se adapta es la Arquitectura Orientada a Recursos (ROA).
En RESTful hay principios para diseñar, idempotente no significa que el resultado no pueda cambiar, ya que leí en 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 alimentándola con algunos datos que son servidos por una API RESTful, ejecutar el mismo GET podría 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.
En una arquitectura orientada a recursos (llamémosle ROA de ahora en adelante por brevedad) nos enfocamos en el recurso que podría ser muchas cosas:
Lo que lo hace único en términos de recursos es la capacidad de dirección, lo que significa que solo tiene un URI
De esa manera, la búsqueda encaja perfectamente en RESTful teniendo en cuenta el ROA . Tenemos que usar GET porque supongo que su búsqueda es una búsqueda normal y no cambia nada, por lo que es idempotente (incluso si devuelve cosas diferentes dependiendo de 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 direccionamiento de ROA. ¿Como es eso? Bueno, si envía los filtros de búsqueda en el cuerpo o encabezado, el recurso no es DIRECCIONABLE.
Puede encontrar los principios de lo que es exactamente y URI en el documento original W3:
https://www.w3.org/DesignIssues/Axioms
Cualquier URL en esta arquitectura tiene que ser autodescriptiva. Es necesario si sigue los principios para abordar todo en el URI, significa que puede usar / (barra oblicua) para separar lo que necesite o consultar parámetros. Sabemos que hay limitaciones en eso, pero este es el patrón de arquitectura.
Siguiendo el patrón de ROA en RESTful, 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:
http://myapi.com/sum/1/2
Donde sum, 1 y 2 pueden modificarse, pero el resultado del cálculo es único y es accesible, 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.
fuente
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 un mismo URI viola la idempotencia / seguridad y el principio CoolURI y, en consecuencia, no es RESTful . Es posible que los verbos idempotentes escriban 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 obtener con un GET posterior). Este resultado puede ser jerárquico (más referencias 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 las personas lo hacen de manera diferente. No necesita explicarme lo conveniente que puede ser violar REST.
fuente