web-dev-qa-db-fra.com

REST API Méthode recommandée: comment accepter une liste de valeurs de paramètres en entrée

Nous lançons une nouvelle API REST et je souhaitais connaître l'opinion de la communauté sur les meilleures pratiques en matière de formatage des paramètres d'entrée:

Actuellement, notre API est très centrée sur JSON (ne renvoie que JSON). La question de savoir si nous voulons/devons renvoyer 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 pensais que cela pourrait être pratique pour certains mais étrange en général.

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

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

Devrions-nous simplifier ceci en tant que:

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

Ou est-il utile d'avoir une entrée JSON? Plus d'une douleur?

Nous voudrons peut-être accepter les deux styles, mais cette flexibilité cause-t-elle davantage de confusion et de maux de tête (maintenabilité, documentation, etc.)?

Un cas plus complexe se présente lorsque nous voulons offrir des intrants plus complexes. Par exemple, si nous voulons autoriser plusieurs filtres sur la recherche:

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

Nous ne souhaitons pas nécessairement définir les types de filtres (par exemple, productType et color) sous la forme de noms de requête tels que:

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, est-ce vraiment important? Il est probable que le nombre d’utils JSON soit si important que le type d’entrée n’importe pas.

Je connais nos clients JavaScript qui font AJAX appels à l'API peuvent apprécier les entrées JSON pour leur faciliter la vie.

373
whatupwilly

Un pas en arrière

D'abord et avant tout, REST décrit un URI en tant qu'ID universellement unique. Beaucoup trop de gens sont pris dans la structure des URI et lesquels URI sont plus "reposants" que d'autres. Cet argument est aussi ridicule que de nommer quelqu'un "Bob" vaut mieux que de le nommer "Joe" - les deux noms obtiennent le travail consistant à "identifier une personne". Un URI n'est rien d'autre qu'un nom universellement unique .

Donc, dans les yeux de REST, discuter de savoir si ?id=["101404","7267261"] est plus reposant que ?id=101404,7267261 ou \Product\101404,7267261 est un peu futile.

Cela dit, maintes fois, la manière dont les URI sont construits peut généralement servir de bon indicateur pour d’autres problèmes dans un service RESTful. Il y a deux ou trois drapeaux rouges dans vos URI et vos questions en général.

Suggestions

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

    Nous voudrons peut-être accepter les deux styles, mais cette flexibilité cause-t-elle davantage 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 vous ne pouvez pas avoir deux URI pointant sur la même ressource , mais il existe des moyens bien définis pour le faire. Si vous décidez d'utiliser à la fois les formats JSON et ceux basés sur des listes (ou tout autre format), vous devez choisir lequel de ces formats est l'URI principal canonique . Toutes les réponses à d'autres URI pointant vers la même "ressource" doivent inclure le Content-Location Header .

    En ce qui concerne l’analogie avec le nom, avoir plusieurs adresses URI revient à avoir des surnoms pour des personnes. C'est parfaitement acceptable et souvent très pratique, cependant, si j'utilise un pseudo, je veux probablement connaître leur nom complet - la manière "officielle" de faire référence à cette personne. Ainsi, lorsque quelqu'un mentionne quelqu'un par son nom complet, "Nichloas Telsa", je sais qu'il parle de la même personne que je nomme "Nick".

  2. "Rechercher" dans votre URI

    Un cas plus complexe se présente lorsque nous voulons offrir des intrants plus complexes. Par exemple, si nous voulons autoriser plusieurs filtres sur la recherche ...

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

    Pour vaincre l'analogie du nom, utiliser un verbe dans un URI revient à changer le nom d'une personne lorsque vous souhaitez interagir avec elle. Si j'interagis avec Bob, son nom ne deviendra pas "BobHi" lorsque je veux lui dire bonjour. De même, lorsque nous voulons "rechercher" des produits, notre structure d'URI ne doit pas passer de "/ Product/..." à "/ Search/...".

Répondre à votre question initiale

  1. En ce qui concerne ["101404","7267261"] vs 101404,7267261: Ma suggestion est d'éviter la syntaxe JSON par souci de simplicité (c'est-à-dire que vous ne devez pas obliger vos utilisateurs à coder des URL lorsque vous n'êtes pas obligé de le faire). Cela rendra votre API un peu plus utilisable. Mieux encore, comme d'autres l'ont recommandé, utilisez le format standard application/x-www-form-urlencoded car il sera probablement plus familier à vos utilisateurs finaux (par exemple ?id[]=101404&id[]=7267261). Ce n'est peut-être pas "joli", mais Pretty URI ne signifie pas nécessairement des URI utilisables. Cependant, pour réitérer mon point de départ, quand on parle de REST, peu importe. Ne vous attardez pas trop dessus.

  2. Votre exemple d'URI de recherche complexe peut être résolu de la même manière que votre exemple de produit. Je recommanderais de réutiliser le format application/x-www-form-urlencoded car il s'agit déjà d'un standard que beaucoup connaissent déjà. 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'exigence de codage d'URL et de rendre les choses un peu plus standard, cela homogénéise un peu l'API. L'utilisateur sait que s'il souhaite récupérer un produit ou une liste de produits (les deux sont considérés comme une "ressource" unique en termes RESTful), il s'intéresse à /Product/... URI.

321
nategood

La méthode standard pour transmettre une liste de valeurs en tant que paramètres d'URL consiste à les répéter:

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

La plupart des codes de serveur interpréteront cela comme une liste de valeurs, bien que beaucoup aient des simplifications à valeur unique, vous devrez donc peut-être chercher.

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

Si vous avez besoin d’envoyer JSON au serveur, je n’aime pas le voir dans l’URL (qui est dans un format différent). En particulier, les URL ont une taille limitée (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 vos exigences de requête, en recevant un ID (créant essentiellement une ressource de critère de recherche)
  2. GET la recherche en référençant l'ID ci-dessus
  3. dELETE facultativement, si nécessaire, les exigences de la requête, mais notez qu'elles sont disponibles pour une réutilisation.
213
Kathy Van Stone

D'abord:

Je pense que tu peux le faire de 2 façons

http://our.api.com/Product/<id>: si vous voulez juste un enregistrement

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

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

Ou celui que j'aime le plus, c'est:

Vous pouvez utiliser le Hypermedia comme moteur de la propriété State State (HATEOAS) d’un RestFul WS et effectuer un appel http://our.api.com/Product qui doit renvoyer les URL équivalentes de http://our.api.com/Product/<id> et appelez-les ensuite.

Seconde

Lorsque vous devez faire des requêtes sur les appels d'URL. Je suggère d'utiliser de nouveau HATEOAS.

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

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

3) (avec HATEOAS) Faites un appel à ` http://our.api.com/term/pumas/productType/ -> recevez les URL toutes les adresses URL de vêtements possibles -> appelez celles que vous avez want (vêtements et sacs) -> recevez les URL de couleurs possibles -> appelez celles que vous voulez

19
Diego Dias

Vous voudrez peut-être vérifier RFC 657 . Cette spécification de modèle d'URI montre de nombreux exemples de la manière dont les URL peuvent contenir des paramètres.

9
Darrel Miller

Premier cas:

Une recherche de produit normale ressemblerait à ceci

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

Donc, je pense que la meilleure pratique serait de le faire

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

deuxième cas

Rechercher avec les paramètres de chaîne de requête - très bien comme ça. Je serais tenté de combiner les termes avec AND et OR au lieu d'utiliser [].

PS Cela peut être subjectif, alors faites ce avec quoi vous vous sentez à l'aise.

La raison pour laquelle les données sont placées dans l'URL est que le lien peut être collé sur un site/partagé entre les utilisateurs. S'il ne s'agit pas d'un problème, utilisez plutôt un JSON/POST.

EDIT: À la réflexion, je pense que cette approche convient à une entité avec une clé composée, mais pas à une requête pour plusieurs entités.

7
James Westgate

Je me rangerai avec la réponse de nategood car elle est complète et semble avoir plu à vos besoins. Cependant, j'aimerais ajouter un commentaire sur l'identification de plusieurs (1 ou plus) ressources de cette façon:

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

ce faisant, vous:

Complexifiez les clients en les forçant à interpréter votre réponse comme un tableau, ce qui pour moi est contre-intuitif si je fais la demande suivante: http://our.api.com/Product/101404

Créez des API redondantes avec une API pour obtenir tous les produits et celle ci-dessus pour en obtenir un ou plusieurs. Etant donné que vous ne devriez pas montrer plus d'une page de détails à un utilisateur au nom de l'UX, je pense qu'avoir plus d'un ID serait inutile et purement utilisé pour filtrer les produits.

Cela ne pose peut-être pas problème, mais vous devrez soit gérer vous-même ce côté serveur en renvoyant une seule entité (en vérifiant si votre réponse en contient une ou plusieurs), soit laisser les clients la gérer.

Exemple

Je veux commander un livre chez Amazing . Je sais exactement de quel livre il s'agit et je le vois dans la liste lors de la navigation dans les livres d'Horror:

  1. 10 000 lignes étonnantes, 0 test incroyable
  2. Le retour de l'incroyable monstre
  3. Reproduisons le code incroyable
  4. Le début étonnant de la fin

Après avoir sélectionné le deuxième livre, je suis redirigé vers une page détaillant la partie livre d’une liste:

--------------------------------------------
Book #1
--------------------------------------------
    Title: The return of the amazing monster
    Summary:
    Pages:
    Publisher:
--------------------------------------------

Ou dans une page me donnant les détails complets de ce livre seulement?

---------------------------------
The return of the amazing monster
---------------------------------
Summary:
Pages:
Publisher:
---------------------------------

Mon avis

Je suggérerais d'utiliser l'ID dans la variable de chemin lorsque l'unicité est garantie lors de l'obtention des détails de cette ressource. Par exemple, les API ci-dessous suggèrent plusieurs manières d'obtenir les détails d'une ressource spécifique (en supposant qu'un produit ait un ID unique et qu'une spécification pour ce produit porte un nom unique et que vous puissiez naviguer de haut en bas):

/products/{id}
/products/{id}/specs/{name}

Au moment où vous avez besoin de plus d'une ressource, je suggère de filtrer à partir d'une plus grande collection. Pour le même exemple:

/products?ids=

Bien sûr, c’est mon avis, car cela n’est pas imposé.

0
gumol