siglas - Diseño de URL RESTful para búsqueda
rest vs soap (12)
Además también sugeriría:
/cars/search/all{?color,model,year}
/cars/search/by-parameters{?color,model,year}
/cars/search/by-vendor{?vendor}
Aquí, la Search
se considera como un recurso secundario del recurso Cars
.
Estoy buscando una forma razonable de representar las búsquedas como una URL RESTful.
La configuración: Tengo dos modelos, Cars y Garages, donde los Cars pueden estar en Garages. Así que mis urls se parecen a
/car/xxxx
xxx == car id
returns car with given id
/garage/yyy
yyy = garage id
returns garage with given id
Un automóvil puede existir por sí solo (de ahí el / automóvil), o puede existir en un garaje. ¿Cuál es la forma correcta de representar, digamos, todos los autos en un garaje determinado? Algo como:
/garage/yyy/cars ?
¿Qué hay de la unión de los coches en el garaje yyy y zzz?
¿Cuál es la forma correcta de representar una búsqueda de automóviles con ciertos atributos? Diga: muéstrame todos los sedanes azules con 4 puertas:
/car/search?color=blue&type=sedan&doors=4
¿O debería ser / coches en su lugar?
El uso de "búsqueda" parece inapropiado allí, ¿cuál es una mejor forma / término? Debería ser:
/cars/?color=blue&type=sedan&doors=4
¿Deben los parámetros de búsqueda ser parte de PATHINFO o QUERYSTRING?
En resumen, estoy buscando orientación para el diseño de url REST de modelos cruzados y para búsqueda.
[Actualización] Me gusta la respuesta de Justin, pero él no cubre el caso de búsqueda en varios campos:
/cars/color:blue/type:sedan/doors:4
o algo así. Como vamos de
/cars/color/blue
al caso de campos múltiples?
Aunque me gusta la respuesta de Justin, siento que representa con más precisión un filtro en lugar de una búsqueda. ¿Qué pasa si quiero saber acerca de los coches con nombres que comienzan con cam?
De la forma en que lo veo, podría incorporarlo a la forma en que maneja recursos específicos:
/ coches / cam *
O, simplemente puede agregarlo en el filtro:
/ coches / puertas / 4 / nombre / cámara * / colores / rojo, azul, verde
Personalmente, prefiero este último, sin embargo, de ninguna manera soy un experto en REST (habiendo oído hablar de él hace apenas unas 2 semanas ...)
Aunque tener los parámetros en la ruta tiene algunas ventajas, existen, IMO, algunos factores de peso.
No todos los caracteres necesarios para una consulta de búsqueda están permitidos en una URL. La mayoría de los caracteres de puntuación y Unicode tendrían que estar codificados en la URL como un parámetro de cadena de consulta. Estoy luchando con el mismo problema. Me gustaría usar XPath en la URL, pero no toda la sintaxis de XPath es compatible con una ruta URI. Por lo tanto, para rutas simples,
/cars/doors/driver/lock/combination
sería apropiado para ubicar el elemento "combination
" en el documento XML de la puerta del conductor. Pero/car/doors[id=''driver'' and lock/combination=''1234'']
no es tan amigable.Hay una diferencia entre filtrar un recurso en función de uno de sus atributos y especificar un recurso.
Por ejemplo, desde
/cars/colors
devuelve una lista de todos los colores para todos los autos (el recurso devuelto es una colección de objetos de color)/cars/colors/red,blue,green
devolvería una lista de objetos de color que son rojos, azules o verdes, no una colección de autos.Para devolver los coches, el camino sería
/cars?color=red,blue,green
o/cars/search?color=red,blue,green
Los parámetros en la ruta son más difíciles de leer porque los pares nombre / valor no están aislados del resto de la ruta, que no son pares nombre / valor.
Un último comentario. Prefiero /garages/yyy/cars
(siempre plural) a /garage/yyy/cars
(quizás fue un error tipográfico en la respuesta original) porque evita cambiar el camino entre singular y plural. Para palabras con una ''s'' agregada, el cambio no es tan malo, pero cambiar /person/yyy/friends
a /people/yyy
parece incómodo.
El diseño bonito de RESTful URL se trata de mostrar un recurso basado en una estructura (estructura similar a un directorio, fecha: artículos / -id
, objeto y sus atributos, ..), la barra /
indica la estructura jerárquica, use el -id
en lugar.
Estructura jerarquica
Personalmente preferiría:
/garage-id/cars/car-id
/cars/car-id #for cars not in garages
Si un usuario elimina la parte /car-id
, trae la vista previa de los cars
intuitiva. El usuario sabe exactamente dónde está en el árbol, qué está mirando. Él sabe desde el primer vistazo, que los garajes y los coches están en relación. /car-id
también denota que pertenece a diferencia de /car/id
.
buscando
La consulta está bien tal como está , solo tiene su preferencia, lo que debe tenerse en cuenta. La parte divertida viene cuando se unen búsquedas (ver más abajo).
/cars?color=blue;type=sedan #most prefered by me
/cars;color-blue+doors-4+type-sedan #looks good when using car-id
/cars?color=blue&doors=4&type=sedan #I don''t recommend using &*
O básicamente cualquier cosa que no sea una barra como se explicó anteriormente.
La fórmula: /cars[?;]color[=-:]blue[,;+&]
, * aunque no usaría el signo &
ya que es irreconocible desde el texto a primera vista.
** ¿Sabía que pasar el objeto JSON en URI es RESTful? **
Listas de opciones
/cars?color=black,blue,red;doors=3,5;type=sedan #most prefered by me
/cars?color:black:blue:red;doors:3:5;type:sedan
/cars?color(black,blue,red);doors(3,5);type(sedan) #does not look bad at all
/cars?color:(black,blue,red);doors:(3,5);type:sedan #little difference
posibles características?
Negar las cadenas de búsqueda (!)
Para buscar cualquier coche, pero no negro y rojo :
?color=!black,!red
color:(!black,!red)
Búsquedas unidas
Busque autos rojos o azules o negros con 3 puertas en garajes id 1..20 o 101..103 o 999 pero no 5 /garage[id=1-20,101-103,999,!5]/cars[color=red,blue,black;doors=3]
A continuación, puede construir consultas de búsqueda más complejas. (Observe la coincidencia de atributos de CSS3 para la idea de hacer coincidir subcadenas. Por ejemplo, buscar usuarios que contengan "bar" user*=bar
)
Conclusión
De todos modos, esta podría ser la parte más importante para usted, ya que puede hacerlo como quiera después de todo, solo tenga en cuenta que el URI RESTful representa una estructura que se entiende fácilmente, por ejemplo, directorio- /directory/file
, /collection/node/item
, dates /articles/{year}/{month}/{day}
.. Y cuando omite cualquiera de los últimos segmentos, inmediatamente sabrá lo que obtiene.
Entonces ..., todos estos personajes son permitidos sin codificar :
- sin reservas:
a-zA-Z0-9_.-~
- reservado
;/?:@=&$-_.+!*''(),
- inseguro *:
<>"#%{}|/^~[]`
* Por qué no es seguro y por qué debería estar codificado: RFC 1738 ver 2.2
RFC 3986 ver 2.2
A pesar de lo que dije anteriormente, aquí hay una distinción común de delimitadores, lo que significa que algunos "son" más importantes que otros.
- delimitadores genéricos::
:/?#[]@
- sub-delimitadores:!
!$&''()*+,;=
Más lectura:
Jerarquía: ver 2.3 , ver 1.2.3
doriantaylor.com/policy/http-url-path-parameter-syntax
Asignación de atributos CSS3
IBM: servicios web RESTful - Lo básico
Nota: RFC 1738 fue actualizado por RFC 3986
Esto no es REST. No puede definir URI para recursos dentro de su API. La navegación de recursos debe ser impulsada por hipertexto. Está bien si desea URI bonitas y grandes cantidades de acoplamiento, pero simplemente no lo llame REST, porque viola directamente las restricciones de la arquitectura RESTful.
Ver este article del inventor de REST.
Hay muchas buenas opciones para su caso aquí. Aún así debería considerar el uso del cuerpo POST.
La cadena de consulta es perfecta para su ejemplo, pero si tiene algo más complicado, por ejemplo, una larga lista arbitraria de elementos o condicionales booleanos, es posible que desee definir la publicación como un documento, que el cliente envía por POST.
Esto permite una descripción más flexible de la búsqueda, así como evita el límite de longitud de la URL del servidor.
La respuesta de Justin es probablemente el camino a seguir, aunque en algunas aplicaciones podría tener sentido considerar una búsqueda en particular como un recurso por derecho propio, por ejemplo, si desea admitir búsquedas guardadas con nombre:
/search/{searchQuery}
o
/search/{savedSearchName}
Mi consejo sería este:
/garages
Returns list of garages (think JSON array here)
/garages/yyy
Returns specific garage
/garage/yyy/cars
Returns list of cars in garage
/garages/cars
Returns list of all cars in all garages (may not be practical of course)
/cars
Returns list of all cars
/cars/xxx
Returns specific car
/cars/colors
Returns lists of all posible colors for cars
/cars/colors/red,blue,green
Returns list of cars of the specific colors (yes commas are allowed :) )
Editar:
/cars/colors/red,blue,green/doors/2
Returns list of all red,blue, and green cars with 2 doors.
/cars/type/hatchback,coupe/colors/red,blue,green/
Same idea as the above but a lil more intuitive.
/cars/colors/red,blue,green/doors/two-door,four-door
All cars that are red, blue, green and have either two or four doors.
Esperemos que eso te dé la idea. Esencialmente, su API Rest debe ser fácilmente detectable y debe permitirle navegar por sus datos. Otra ventaja de usar URL y no cadenas de consulta es que puede aprovechar los mecanismos de almacenamiento en caché nativos que existen en el servidor web para el tráfico HTTP.
Aquí hay un enlace a una página que describe los males de las cadenas de consulta en REST: http://web.archive.org/web/20070815111413/http://rest.blueoxen.net/cgi-bin/wiki.pl?QueryStringsConsideredHarmful
Usé el caché de Google porque la página normal no funcionaba para mí aquí también está ese enlace: http://rest.blueoxen.net/cgi-bin/wiki.pl?QueryStringsConsideredHarmful
Para ampliar la respuesta de Peter, podría hacer de Search un recurso de primera clase:
POST /searches # create a new search
GET /searches # list all searches (admin)
GET /searches/{id} # show the results of a previously-run search
DELETE /searches/{id} # delete a search (admin)
El recurso de búsqueda tendría campos para el color, modelo, estado empaquetado, etc. y podría especificarse en XML, JSON o cualquier otro formato. Al igual que el recurso de Automóviles y Garajes, podría restringir el acceso a las búsquedas en función de la autenticación. Los usuarios que ejecutan con frecuencia las mismas búsquedas pueden almacenarlos en sus perfiles para que no tengan que volver a crearlos. Las URL serán lo suficientemente cortas para que, en muchos casos, puedan intercambiarse fácilmente por correo electrónico. Estas búsquedas almacenadas pueden ser la base de fuentes RSS personalizadas, y así sucesivamente.
Hay muchas posibilidades para usar Búsquedas cuando las consideras como recursos.
La idea se explica con más detalle en este Railscast .
Para la búsqueda, utilice cadenas de consulta. Esto es perfectamente REST.
/cars?color=blue&type=sedan&doors=4
Una ventaja de las cadenas de consulta regulares es que son estándar y ampliamente entendidas y que pueden generarse a partir de la obtención de formularios.
RESTful no recomienda el uso de verbos en URL / cars / search no es reparador. La forma correcta de filtrar / buscar / paginar su API es a través de los Parámetros de Consulta. Sin embargo, puede haber casos en los que hay que romper la norma. Por ejemplo, si está buscando en múltiples recursos, entonces tiene que usar algo como / search? Q = query
Puede visitar http://saipraveenblog.wordpress.com/2014/09/29/rest-api-best-practices/ para comprender las mejores prácticas para diseñar APIs RESTful
Yo uso dos enfoques para implementar búsquedas.
1) El caso más simple, para consultar elementos asociados, y para la navegación.
/cars?q.garage.id.eq=1
Esto significa, consultar los coches que tienen ID de garaje igual a 1.
También es posible crear búsquedas más complejas:
/cars?q.garage.street.eq=FirstStreet&q.color.ne=red&offset=300&max=100
Autos en todos los garajes en FirstStreet que no son rojos (3ra página, 100 elementos por página).
2) Las consultas complejas se consideran recursos regulares que se crean y se pueden recuperar.
POST /searches => Create
GET /searches/1 => Recover search
GET /searches/1?offset=300&max=100 => pagination in search
El cuerpo POST para la creación de búsquedas es el siguiente:
{
"$class":"test.Car",
"$q":{
"$eq" : { "color" : "red" },
"garage" : {
"$ne" : { "street" : "FirstStreet" }
}
}
}
Se basa en Grails (criterios DSL): http://grails.org/doc/2.4.3/ref/Domain%20Classes/createCriteria.html