Pregunta

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. Entonces mis urls parecen:

/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 autos 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 / cars en su lugar?

El uso de " búsqueda " Parece inapropiado ahí, ¿cuál es una mejor manera / término? Si solo fuera:

/cars/?color=blue&type=sedan&doors=4

¿Deben los parámetros de búsqueda formar parte de PATHINFO o QUERYSTRING?

En resumen, estoy buscando orientación para el diseño de url REST de modelos cruzados y para la 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í. ¿Cómo vamos desde

/cars/color/blue

¿al caso de campos múltiples?

¿Fue útil?

Solución

Para la búsqueda, use cadenas de consulta. Esto es perfectamente RESTful:

/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 desde el formulario-get.

Otros consejos

El RESTful pretty URL design se trata de mostrar un recurso basado en una estructura (estructura tipo directorio, fecha: artículos / 2005/5/13, objeto y sus atributos, ...), la barra inclinada / indica una estructura jerárquica, use el -id en su lugar.

Estructura jerárquica

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 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 automóviles están en relación. / car-id también denota que pertenece a diferencia de / car / id .

Buscando

La consulta de búsqueda 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 [,; + & amp;] , * aunque no usaría el signo & amp; como 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 cadenas de búsqueda (!)
Para buscar coches, pero no negro y rojo :
? color =! black,! red
color :(! negro,! rojo)

Búsquedas unidas
Busque rojo o azul o negro con 3 puertas en garages 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. (Mire en correspondencia 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-como / directory / file , / collection / node / item , fechas / articles / {year} / {month} / {day} .. Y cuando omites cualquiera de los últimos segmentos, inmediatamente sabes lo que obtienes.

Entonces ..., todos estos caracteres están permitidos sin codificar :

  • sin reservas: a-zA-Z0-9 _.- ~
  • reservado: ; /?: @ = & amp; $ -_. +! * '(),
  • inseguro *: <>"#%{}|\^~??`

* Por qué no es seguro y por qué debería estar codificado: RFC 1738 vea 2.2

RFC 3986 vea 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: :/?#??@
  • subdelimetros: !$&'()*+,;=

Más lecturas:
Jerarquía: vea 2.3 , ver 1.2.3
sintaxis de los parámetros de la ruta url
correspondencia de atributos de CSS3
IBM: servicios web RESTful: conceptos básicos
Nota: RFC 1738 fue actualizado por RFC 3986

Aunque tener los parámetros en la ruta tiene algunas ventajas, hay, 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 / Combinación sería apropiado para ubicar el elemento ' combinación ' 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 autos, la ruta 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 en plural) a / garage / yyy / cars (quizás fue un error tipográfico en la respuesta original) porque evita cambiar la ruta 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 engorroso.

Para ampliar la respuesta de Peter, podría hacer que Search sea 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, marca del modelo, estado empaquetado, etc. y podría especificarse en XML, JSON o cualquier otro formato. Al igual que el recurso de Car and Garage, puede 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 de usar Búsquedas cuando piensas que son recursos.

La idea se explica con más detalle en este Railscast .

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}

Esto no es REST. No puede definir URI para recursos dentro de su API. La navegación de recursos debe ser controlada por hipertexto Está bien si quieres URI bonitas y grandes cantidades de acoplamiento, pero simplemente no lo llames REST, porque viola directamente las restricciones de la arquitectura RESTful.

Vea este artículo del inventor de REST.

Utilizo 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 automóviles que tengan una 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 pueden recuperarse.

    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

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 ...)

RESTful no recomienda el uso de verbos en URL / cars / search no es reparador. La forma correcta de filtrar / buscar / paginar sus API's 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 ir a través de http: //saipraveenblog.wordpress .com / 2014/09/29 / rest-api-best-practice / para comprender las mejores prácticas para diseñar APIs RESTful

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í, Search se considera como un recurso secundario del recurso Cars .

Aquí hay muchas buenas opciones para su caso. Aún así, deberías considerar usar el cuerpo POST.

La cadena de consulta es perfecta para tu ejemplo, pero si tienes algo más complicado, por ejemplo. una lista larga arbitraria de elementos o condicionales booleanos, es posible que desee definir la publicación como un documento, que el cliente envía a través de 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.

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

Utilicé el caché de Google porque la página normal no funcionaba para mí. Este es el enlace también: http://rest.blueoxen.net/cgi-bin/wiki.pl? QueryStringsConsideredHarmful

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top