web-dev-qa-db-fra.com

Conception d'URL RESTful pour la recherche

Je recherche un moyen raisonnable de représenter les recherches sous forme d'URL RESTful.

La configuration: j'ai deux modèles, Cars et Garages, où les voitures peuvent être dans les garages. Donc, mes urls ressemblent à:

/car/xxxx
  xxx == car id
  returns car with given id

/garage/yyy
  yyy = garage id
  returns garage with given id

Une voiture peut exister seule (d'où la/voiture) ou dans un garage. Quelle est la bonne façon de représenter, par exemple, toutes les voitures dans un garage donné? Quelque chose comme:

/garage/yyy/cars     ?

Que diriez-vous de l'union des voitures dans garage yyy et zzz?

Quelle est la bonne façon de représenter une recherche de voitures avec certains attributs? Dis: montre-moi toutes les berlines bleues à 4 portes:

/car/search?color=blue&type=sedan&doors=4

ou devrait-il être/cars à la place?

L'utilisation de "recherche" semble inappropriée là-bas - quelle est la meilleure façon/terme? Devrait-il simplement être:

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

Les paramètres de recherche doivent-ils faire partie de PATHINFO ou de QUERYSTRING?

En bref, je recherche des conseils pour la conception d’URL entre modèles REST et pour la recherche.

[Mise à jour] J'aime la réponse de Justin, mais il ne couvre pas le cas de recherche multi-champs:

/cars/color:blue/type:sedan/doors:4

ou quelque chose comme ça. Comment allons-nous de

/cars/color/blue

au cas des champs multiples?

387
Parand

Pour la recherche, utilisez des chaînes de requête. C'est parfaitement reposant:

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

Un avantage des chaînes de requêtes régulières est qu'elles sont standard et largement comprises et qu'elles peuvent être générées à partir de form-get.

396
pbreitenbach

La jolie conception d'URL RESTful consiste à afficher une ressource basée sur une structure (structure de type répertoire, date: articles/2005/5/13, objet et ses attributs, ..), la barre oblique / indique une structure hiérarchique, utilisez plutôt -id.

Structure hiérarchique

Personnellement, je préférerais:

/garage-id/cars/car-id
/cars/car-id   #for cars not in garages

Si un utilisateur supprime la partie /car-id, l'aperçu cars devient intuitif. L'utilisateur sait exactement où il se trouve dans l'arbre et ce qu'il regarde. Il sait dès le premier regard que garages et voitures sont en relation. /car-id indique également qu'il appartient ensemble contrairement à /car/id.

Recherche

La requête de recherche est correcte telle quelle , il n'y a que votre préférence, ce qui doit être pris en compte. La partie amusante vient lors de la jointure de recherches (voir ci-dessous).

/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 &*

Ou fondamentalement tout ce qui n'est pas une barre oblique, comme expliqué ci-dessus.
La formule: /cars[?;]color[=-:]blue[,;+&], * bien que je n’utilise pas le signe & car il est à première vue méconnaissable du texte.

** Saviez-vous que passer un objet JSON dans l'URI est RESTful? **

Listes d'options

/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

fonctionnalités possibles?

Négocie les chaînes de recherche (!)
Pour rechercher des voitures, mais pas noir et rouge:
?color=!black,!red
color:(!black,!red)

Recherches jointes
Recherche rouge ou bleu ou noir voitures avec 3 portes dans les garages id 1..20 ou 101..103 ou 999 mais pas 5 /garage[id=1-20,101-103,999,!5]/cars[color=red,blue,black;doors=3]
Vous pouvez ensuite construire des requêtes de recherche plus complexes. (Regardez correspondance des attributs CSS pour l’idée de faire correspondre les sous-chaînes. Par exemple, rechercher des utilisateurs contenant "bar" user*=bar.)

Conclusion

Quoi qu’il en soit, c’est peut-être la partie la plus importante pour vous, car vous pouvez le faire comme vous le souhaitez après tout, mais n'oubliez pas que RESTful URI représente structure facile à comprendre, par exemple comme un répertoire /directory/file, /collection/node/item, dates /articles/{year}/{month}/{day} .. Et lorsque vous omettez l'un des derniers segments, vous savez immédiatement ce que vous obtenez.

Donc .., tous ces caractères sont autorisés non codés :

  • sans réserve: a-zA-Z0-9_.-~
  • réservé: ;/?:@=&$-_.+!*'(),
  • dangereux *: <>"#%{}|\^~[]`

* Pourquoi non sécurisé et pourquoi devrait plutôt être codé: RFC 1738 voir 2.2

RFC 3986 voir 2.2
Malgré ce que j'ai déjà dit, voici une distinction courante des délimètres, ce qui signifie que certains "sont" plus importants que d'autres.

  • délimètres génériques: :/?#[]@
  • sous-délimètres: !$&'()*+,;=

Plus de lecture:
Hiérarchie: voir 2. , voir 1.2.
syntaxe du paramètre de chemin d'accès
correspondance d'attribut CSS
IBM: services Web RESTful - Notions de base
Remarque: la RFC 1738 a été mise à jour par la RFC 3986.

113
Qwerty

Bien que le fait d'avoir les paramètres dans le chemin présente certains avantages, il existe, IMO, des facteurs qui l'emportent.

  • Tous les caractères nécessaires à une requête de recherche ne sont pas autorisés dans une URL. La plupart des caractères de ponctuation et Unicode doivent être encodés en URL en tant que paramètre de chaîne de requête. Je suis aux prises avec le même problème. J'aimerais utiliser XPath dans l'URL, mais toute la syntaxe XPath n'est pas compatible avec un chemin d'accès URI. Donc, pour les chemins simples, /cars/doors/driver/lock/combination conviendrait pour localiser l'élément 'combination' dans le document XML de la porte du conducteur. Mais /car/doors[id='driver' and lock/combination='1234'] n'est pas si amical.

  • Il existe une différence entre filtrer une ressource en fonction de l'un de ses attributs et spécifier une ressource. 

    Par exemple, depuis

    /cars/colors renvoie une liste de toutes les couleurs pour toutes les voitures (la ressource renvoyée est une collection d'objets de couleur)

    /cars/colors/red,blue,green renverrait une liste d'objets de couleur rouges, bleus ou verts, et non une collection de voitures.

    Pour rendre les voitures, le chemin serait

    /cars?color=red,blue,green ou /cars/search?color=red,blue,green

  • Les paramètres du chemin sont plus difficiles à lire car les paires nom/valeur ne sont pas isolées du reste du chemin, ce qui n'est pas des paires nom/valeur. 

Un dernier commentaire. Je préfère /garages/yyy/cars (toujours au pluriel) à /garage/yyy/cars (peut-être était-ce une faute de frappe dans la réponse originale), car cela évite de changer le chemin entre singulier et pluriel. Pour les mots avec un «s» ajouté, le changement n’est pas si grave, mais changer de /person/yyy/friends à /people/yyy semble fastidieux.

34
Doug Domeny

Pour développer la réponse de Peter, vous pourriez faire de la recherche une ressource de premier ordre:

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)

La ressource de recherche aurait des champs pour la couleur, le modèle, l'état garaged, etc., et pourrait être spécifiée en XML, JSON ou tout autre format. A l'instar des ressources Car et Garage, vous pouvez limiter l'accès aux recherches en fonction de l'authentification. Les utilisateurs qui exécutent fréquemment les mêmes recherches peuvent les stocker dans leurs profils afin de ne pas avoir à les recréer. Les URL seront suffisamment courtes pour que, dans de nombreux cas, elles puissent être facilement échangées par courrier électronique. Ces recherches stockées peuvent constituer la base de flux RSS personnalisés, etc.

Il existe de nombreuses possibilités d'utilisation des recherches lorsque vous les considérez comme des ressources.

L'idée est expliquée plus en détail dans ce Railscast .

30
Rich Apodaca

La réponse de Justin est probablement la voie à suivre, même si dans certaines applications, il peut être judicieux de considérer une recherche particulière comme une ressource à part entière, par exemple si vous souhaitez prendre en charge des recherches enregistrées nommées:

/search/{searchQuery}

ou 

/search/{savedSearchName}
11
Peter Hilton

Ce n'est pas REST. Vous ne pouvez pas définir d'URI pour les ressources au sein de votre API. La navigation dans les ressources doit être hypertexte. C'est bien si vous voulez de jolies adresses URI et des quantités importantes de couplage, mais ne l'appelez pas simplement REST, car elles violent directement les contraintes de l'architecture RESTful.

Voir cet article de l'inventeur de REST.

5
aehlke

J'utilise deux approches pour mettre en œuvre des recherches. 

1) Le cas le plus simple, pour interroger les éléments associés et pour la navigation. 

    /cars?q.garage.id.eq=1

Cela signifie, interroger les voitures dont l'identifiant de garage est égal à 1.

Il est également possible de créer des recherches plus complexes:

    /cars?q.garage.street.eq=FirstStreet&q.color.ne=red&offset=300&max=100

Voitures dans tous les garages de FirstStreet qui ne sont pas rouges (3ème page, 100 éléments par page).

2) Les requêtes complexes sont considérées comme des ressources ordinaires créées et pouvant être récupérées. 

    POST /searches  => Create
    GET  /searches/1  => Recover search
    GET  /searches/1?offset=300&max=100  => pagination in search

Le corps POST pour la création de recherche est le suivant:

    {  
       "$class":"test.Car",
       "$q":{
          "$eq" : { "color" : "red" },
          "garage" : {
             "$ne" : { "street" : "FirstStreet" }
          }
       }
    }

Il est basé à Grails (critères DSL): http://grails.org/doc/2.4.3/ref/Domain%20Classes/createCriteria.html

5
user2108278

En plus je suggérerais aussi:

/cars/search/all{?color,model,year}
/cars/search/by-parameters{?color,model,year}
/cars/search/by-vendor{?vendor}

Ici, Search est considéré comme une ressource enfant de Cars ressource.

1
aux

RESTful ne recommande pas d'utiliser des verbes dans les URL/voitures/la recherche n'est pas reposante. La méthode appropriée pour filtrer/rechercher/paginer vos API consiste à utiliser les paramètres de requête. Cependant, il peut arriver que vous deviez enfreindre la norme. Par exemple, si vous effectuez une recherche sur plusieurs ressources, vous devez utiliser quelque chose comme/search? Q = query

Vous pouvez consulter http://saipraveenblog.wordpress.com/2014/09/29/rest-api-best-practices/ pour comprendre les meilleures pratiques pour la conception d'API RESTful

1
java_geek

Bien que j'aime la réponse de Justin, je pense qu'elle représente plus précisément un filtre plutôt qu'une recherche. Et si je veux en savoir plus sur les voitures dont le nom commence par cam?

Selon moi, vous pourriez l'intégrer à votre façon de gérer des ressources spécifiques:
/cars/cam *

Ou vous pouvez simplement l'ajouter dans le filtre:
/voitures/portes/4/nom/cam */couleurs/rouge, bleu, vert

Personnellement, je préfère ce dernier, mais je ne suis en aucun cas un expert de REST (j'en ai entendu parler pour la première fois il y a à peine 2 semaines ...)

1
Nate

Il y a beaucoup de bonnes options pour votre cas ici. Vous devriez néanmoins envisager d'utiliser le corps POST.

La chaîne de requête est parfaite pour votre exemple, mais si vous avez quelque chose de plus compliqué, par exemple. une longue liste arbitraire d'éléments ou de booléens, vous pouvez définir la publication comme un document que le client envoie via POST.

Cela permet une description plus souple de la recherche et évite la limite de longueur de l'URL du serveur.

0
estani