452 votes

API REST - Meilleure pratique : Comment accepter une liste de valeurs de paramètres en entrée

Nous lançons une nouvelle API REST et je souhaitais obtenir l'avis de la communauté sur les meilleures pratiques concernant le formatage des paramètres d'entrée :

Actuellement, notre API est très axée sur le JSON (elle ne renvoie que du JSON). La question de savoir si nous voulons/devons renvoyer du XML est une question distincte.

Comme la sortie de notre API est centrée sur JSON, nous nous sommes engagés dans une voie où nos entrées sont un peu centrées sur JSON et je me suis dit que cela pouvait être pratique pour certains mais bizarre en général.

Par exemple, pour obtenir quelques détails de produits où plusieurs produits peuvent être tirés à la fois, nous avons actuellement :

http://our.api.com/Product?id=["101404","7267261"]

Devrions-nous simplifier cela comme suit :

http://our.api.com/Product?id=101404,7267261

Ou est-ce que l'entrée JSON est pratique ? Ou est-ce plus pénible ?

Nous pouvons vouloir accepter les deux styles, mais cette flexibilité ne provoque-t-elle pas en fait plus de confusion et de maux de tête (maintenabilité, documentation, etc.) ?

Un cas plus complexe est celui où nous voulons offrir des entrées plus complexes. Par exemple, si nous voulons autoriser des filtres multiples pour la recherche :

http://our.api.com/Search?term=pumas&filters={"productType":["Clothing","Bags"],"color":["Black","Red"]}

Nous ne voulons pas nécessairement mettre les types de filtre (par exemple productType et color) comme noms de requête comme ceci :

http://our.api.com/Search?term=pumas&productType=["Clothing","Bags"]&color=["Black","Red"]

Parce que nous voulions regrouper toutes les entrées de filtres.

En fin de compte, cela a-t-il vraiment de l'importance ? Il est probable qu'il existe tellement d'utilitaires JSON que le type d'entrée n'a pas beaucoup d'importance.

Je sais que nos clients JavaScript effectuant des appels AJAX à l'API peuvent apprécier les entrées JSON qui leur facilitent la vie.

369voto

nategood Points 3753

Un pas en arrière

Tout d'abord, REST décrit un URI comme un identifiant universellement unique. Beaucoup trop de gens s'attardent sur la structure des URI et sur les URI qui sont plus "reposants" que d'autres. Cet argument est aussi ridicule que de dire qu'il vaut mieux nommer quelqu'un "Bob" que "Joe" - les deux noms permettent d'"identifier une personne". Un URI n'est rien d'autre qu'un universellement unique nom.

Donc, aux yeux de REST, se disputer pour savoir si ?id=["101404","7267261"] est plus reposant que ?id=101404,7267261 o \Product\101404,7267261 est quelque peu futile.

Cela dit, la façon dont les URI sont construits peut souvent servir de bon indicateur pour d'autres problèmes dans un service RESTful. Il y a quelques signaux d'alarme dans vos URI et votre question en général.

Suggestions

  1. Plusieurs URI pour la même ressource et Content-Location

    Nous pouvons vouloir accepter les deux styles, mais cette flexibilité ne provoque-t-elle pas en fait plus de confusion et de maux de tête (maintenabilité, documentation, etc.) ?

    Les URI identifient les ressources. Chaque ressource doit avoir un URI canonique. Cela ne signifie pas que deux URI ne peuvent pas pointer vers la même ressource. mais il existe des moyens bien définis pour y parvenir. Si vous décidez d'utiliser à la fois le format JSON et le format basé sur des listes (ou tout autre format), vous devez décider lequel de ces formats est le principal canonique URI. Toutes les réponses à d'autres URI qui pointent vers la même "ressource" doivent inclure l'attribut Content-Location en-tête .

    Pour rester dans l'analogie avec les noms, avoir plusieurs URIs est comme avoir des surnoms pour les gens. C'est tout à fait acceptable et souvent très pratique, mais si j'utilise un surnom, je veux quand même connaître le nom complet de la personne, c'est-à-dire la façon "officielle" de la désigner. Ainsi, lorsque quelqu'un mentionne une personne par son nom complet, "Nichloas Telsa", je sais qu'il s'agit de la même personne que j'appelle "Nick".

  2. "Recherche" dans votre URI

    Un cas plus complexe est celui où nous voulons offrir des entrées plus complexes. Par exemple, si nous voulons autoriser des filtres multiples sur la recherche...

    En règle générale, si votre URI contient un verbe, cela peut être une indication que quelque chose ne va pas. Les URI identifient une ressource, mais ils ne doivent pas indiquer ce que que nous faisons à cette ressource. C'est le rôle de HTTP ou, en termes de repos, de notre "interface uniforme".

    Pour battre en brèche l'analogie avec le nom, utiliser un verbe dans un URI revient à changer le nom d'une personne lorsque vous voulez interagir avec elle. Si je suis en interaction avec Bob, le nom de Bob ne devient pas "BobHi" lorsque je veux lui dire bonjour. De même, lorsque nous voulons "rechercher" des produits, la structure de notre URI ne doit pas passer de "/Produit/..." à "/Recherche/...".

Répondre à votre question initiale

  1. Concernant ["101404","7267261"] vs 101404,7267261 : Ma suggestion ici est d'éviter la syntaxe JSON pour des raisons de simplicité (c'est-à-dire ne pas exiger que les utilisateurs fassent l'encodage des URL lorsque vous n'avez pas vraiment besoin de le faire). Cela rendra votre API un peu plus utilisable. Mieux encore, comme d'autres l'ont recommandé, optez pour la syntaxe standard application/x-www-form-urlencoded car il sera probablement plus familier à vos utilisateurs finaux (par ex. ?id[]=101404&id[]=7267261 ). Ce n'est peut-être pas "joli", mais de jolis URI ne signifient pas nécessairement des URI utilisables. Cependant, pour réitérer mon point de vue initial, en fin de compte, lorsqu'on parle de REST, cela n'a pas d'importance. Ne vous y attardez pas trop.

  2. Votre exemple d'URI de recherche complexe peut être résolu de la même manière que votre exemple de produit. Je vous recommande d'utiliser la méthode application/x-www-form-urlencoded car il s'agit déjà d'un standard avec lequel beaucoup sont familiers. Aussi, je recommanderais de fusionner les deux.

Votre URI...

/Search?term=pumas&filters={"productType":["Clothing","Bags"],"color":["Black","Red"]}    

Votre URI après avoir été encodé en URI...

/Search?term=pumas&filters=%7B%22productType%22%3A%5B%22Clothing%22%2C%22Bags%22%5D%2C%22color%22%3A%5B%22Black%22%2C%22Red%22%5D%7D

Peut être transformé en...

/Product?term=pumas&productType[]=Clothing&productType[]=Bags&color[]=Black&color[]=Red

En plus d'éviter l'obligation d'encoder les URL et de rendre les choses un peu plus standard, cela permet d'homogénéiser un peu l'API. L'utilisateur sait que s'il veut récupérer un produit ou une liste de produits (les deux sont considérés comme une seule "ressource" en termes RESTful), il doit s'intéresser aux éléments suivants /Product/... URIs.

79 votes

Je voulais faire un suivi et noter que le [] n'est pas toujours prise en charge (et bien qu'elle soit courante, elle peut même violer la spécification URI). Certains serveurs HTTP et langages de programmation préfèrent simplement répéter le nom (par ex. productType=value1&productType=value2 ).

1 votes

La question initiale avec cette requête.. "/Recherche?term=pumas&filters={"productType" :["Clothing", "Bags"], "color" :["Black", "Red"]}" se traduit par... (productType==clothing || productType==bags) && (color==black || color==red) MAIS VOTRE SOLUTION : /Product?term=pumas&productType[]=Clothing&productType[]=Bags&color[]=Black&color[]=Red semble se traduire par... Soit (productType==clothing || productType==bags || color==black || color==red) ou Soit (productType==clothing && productType==bags && color==black && color==red) Ce qui me semble un peu différent. Ou ai-je mal compris ?

0 votes

@ThomasCheng Il utilise un objet JSON contenant plusieurs listes. Il peut être utilisé de plusieurs façons, mais il est généralement analysé en un objet correspondant sur le serveur.

279voto

Kathy Van Stone Points 10310

La façon standard de transmettre une liste de valeurs comme paramètres d'URL est de les répéter :

http://our.api.com/Product?id=101404&id=7267261

La plupart des codes de serveur interpréteront ceci comme une liste de valeurs, bien que beaucoup d'entre eux aient des simplifications à valeur unique, ce qui vous obligera peut-être à chercher.

Les valeurs délimitées sont également acceptables.

Si vous devez envoyer du JSON au serveur, je n'aime pas le voir dans l'URL (qui est un format différent). En particulier, les URL ont une limite de taille (en pratique sinon en théorie).

La façon dont j'ai vu certains faire une requête compliquée RESTfully est en deux étapes :

  1. POST les exigences de votre requête, en recevant en retour un ID (créant essentiellement une ressource de critères de recherche)
  2. GET la recherche, en faisant référence à l'ID ci-dessus
  3. éventuellement SUPPRIMER les exigences de la requête si nécessaire, mais notez que ces exigences sont disponibles pour être réutilisées.

8 votes

Merci Kathy. Je pense que je suis d'accord avec vous et que je n'aime pas vraiment voir JSON dans l'URL. Cependant, je ne suis pas fan de faire un post pour une recherche qui est une opération GET inhérente. Pouvez-vous m'indiquer un exemple de cela ?

1 votes

Si les requêtes peuvent fonctionner comme de simples paramètres, faites-le. La source provient de la liste de diffusion rest-discuss : tech.groups.yahoo.com/group/rest-discuss/message/11578

2 votes

Si vous voulez juste montrer deux ressources, la réponse de James Westgate est plus typique

23voto

Diego Dias Points 6879

D'abord :

Je pense que vous pouvez le faire de 2 façons

http://our.api.com/Product/<id> : si vous ne voulez qu'un seul enregistrement

http://our.api.com/Product : si vous voulez tous les enregistrements

http://our.api.com/Product/<id1>,<id2> :comme James l'a suggéré peut être une option puisque ce qui vient après la balise Product est un paramètre.

Ou celui que j'aime le plus est :

Vous pouvez utiliser le L'hypermédia comme moteur de l'état des applications (HATEOAS) d'un WS RestFul et faire un appel http://our.api.com/Product qui devrait retourner les urls équivalentes de http://our.api.com/Product/<id> et les appeler après ça.

Deuxièmement

Quand vous devez faire des requêtes sur les appels url. Je suggère d'utiliser à nouveau HATEOAS.

1) Faites un appel de réception à http://our.api.com/term/pumas/productType/clothing/color/black

2) Faites un appel de réception à http://our.api.com/term/pumas/productType/clothing,bags/color/black,red

3) (En utilisant HATEOAS) Faites un appel get à `. http://our.api.com/term/pumas/productType/ -> recevoir les urls de tous les vêtements possibles -> appeler ceux que vous voulez (vêtements et sacs) -> recevoir les urls de couleurs possibles -> appeler ceux que vous voulez

1 votes

J'ai été mis dans une situation similaire il y a quelques jours, devant régler une api de repos (HATEOAS) pour obtenir une (grande) liste filtrée d'objets et j'ai choisi votre deuxième solution. N'est-ce pas un peu exagéré de rappeler l'api encore et encore pour chaque objet ?

0 votes

Cela dépend vraiment de votre système.... S'il s'agit d'un système simple avec peu d'"options", cela devrait probablement être excessif. Cependant, si vous avez de très grandes listes, il peut devenir très difficile de tout faire en un seul appel, et si votre API est publique, cela peut devenir compliqué pour les utilisateurs (si elle est privée, cela devrait être plus facile... apprenez simplement aux utilisateurs que vous connaissez). Comme alternative, vous pouvez implémenter les deux styles, le HATEOAS et un appel "non-restful array" pour les utilisateurs avancés.

0 votes

Je suis en train de construire un webservice restful api dans rails et j'ai besoin de suivre la même structure d'url que ci-dessus( our.api.com/term/pumas/productType/clothing/color/black ). Mais je ne sais pas comment configurer les routes en conséquence.

13voto

Darrel Miller Points 56797

Vous pourriez vouloir vérifier RFC 6570 . Cette spécification de modèle d'URI montre de nombreux exemples de la façon dont les urls peuvent contenir des paramètres.

1 votes

La section 3.2.8 semble être celle qui s'applique. Il convient toutefois de noter qu'il ne s'agit que d'une proposition de norme et qu'elle ne semble pas avoir dépassé ce stade.

3 votes

@MikePost Maintenant que l'IETF est passé à un processus de maturité en deux étapes pour les documents de la "piste des standards", je m'attends à ce que le 6570 reste ainsi pendant quelques années encore avant de devenir un "standard Internet". tools.ietf.org/html/rfc6410 La spécification est extrêmement stable, comporte de nombreuses mises en œuvre et est largement utilisée.

0 votes

Ah je n'étais pas au courant de ce changement. (Ou, TIL IETF est maintenant plus raisonnable.) Merci !

9voto

James Westgate Points 6789

Premier cas :

Une recherche normale de produit ressemblerait à ceci

http://our.api.com/product/1

Je pense donc que la meilleure pratique serait que vous fassiez ceci

http://our.api.com/Product/101404,7267261

Deuxième cas

Recherche avec les paramètres de la chaîne de recherche - ça marche comme ça. Je serais tenté de combiner les termes avec AND et OR au lieu d'utiliser [] .

PS : cela peut être subjectif, faites donc ce qui vous convient.

La raison de mettre les données dans l'url est que le lien peut être collé sur un site/partagé entre utilisateurs. Si ce n'est pas un problème, utilisez plutôt un JSON/ POST.

EDIT : Après réflexion, je pense que cette approche convient à une entité avec une clé composée, mais pas à une requête portant sur plusieurs entités.

3 votes

Bien sûr, dans les deux exemples, la fin de la ligne / ne devrait pas être là puisque l'URI adresse une ressource, pas une collection.

2 votes

J'ai toujours pensé que les verbes HTTP, dans une utilisation REST, servaient à effectuer des actions spécifiques, et voici la ligne directrice : GET : récupérer/lire un objet, POST : créer un objet, PUT : mettre à jour un objet existant et DELETE : supprimer un objet. Je n'utiliserais donc pas un POST pour récupérer un objet. Si je veux une liste d'objets en particulier (filtre), je ferais un GET avec une liste dans les paramètres url (séparés par une virgule semble bon)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X