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?
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.
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
.
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
.
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
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
.)
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 :
a-zA-Z0-9_.-~
;/?:@=&$-_.+!*'(),
<>"#%{}|\^~[]`
* 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.
:/?#[]@
!$&'()*+,;=
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.
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.
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 .
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}
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.
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
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.
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
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 ...)
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.