API REST Meilleures pratiques: où mettre les paramètres? [fermé]

348

Une API REST peut avoir des paramètres d'au moins deux manières:

  1. Dans le cadre du chemin URL (ie /api/resource/parametervalue )
  2. Comme argument de requête (ie /api/resource?parameter=value )

Quelle est la meilleure pratique ici? Existe-t-il des directives générales sur l'utilisation de 1 et sur l'utilisation de 2?

Exemple réel: Twitter utilise des paramètres de requête pour spécifier des intervalles. ( http://api.twitter.com/1/statuses/home_timeline.json?since_id=12345&max_id=54321)

Serait-il considéré comme une meilleure conception de placer ces paramètres dans le chemin URL?

Kalle Gustafsson
la source

Réponses:

254

S'il existe des bonnes pratiques documentées, je ne les ai pas encore trouvées. Cependant, voici quelques directives que j'utilise pour déterminer où placer les paramètres dans une URL:

Les paramètres facultatifs ont tendance à être plus faciles à mettre dans la chaîne de requête.

Si vous souhaitez renvoyer une erreur 404 lorsque la valeur du paramètre ne correspond pas à une ressource existante, je tends vers un paramètre de segment de chemin. par exemple, /customer/232où 232 n'est pas un identifiant client valide.

Si toutefois vous souhaitez renvoyer une liste vide, lorsque le paramètre n'est pas trouvé, je suggère d'utiliser des paramètres de chaîne de requête. par exemple/contacts?name=dave

Si un paramètre affecte une sous-arborescence entière de votre espace URI, utilisez un segment de chemin. par exemple, un paramètre de langue /en/document/foo.txt par rapport à/document/foo.txt?language=en

Je préfère que les identificateurs uniques soient dans un segment de chemin plutôt que dans un paramètre de requête.

Les règles officielles pour les URI se trouvent dans cette spécification RFC ici . Il y a aussi une autre spécification RFC très utile ici que les règles de définit pour URIs paramétrage.

Darrel Miller
la source
5
Les URI des règles officielles et le projet de sepc étaient vraiment utiles et intéressants! :-)
KajMagnus
1
Le test d'erreur 404 m'aide beaucoup à éviter de mettre des informations dans le chemin qui appartient aux paramètres de requête, aux en-têtes ou au corps de la demande. Merci de l'avoir signalé!
Kevin Condon
152

Réponse tardive, mais j'ajouterai un aperçu supplémentaire de ce qui a été partagé, à savoir qu'il existe plusieurs types de "paramètres" à une demande, et vous devez en tenir compte.

  1. Localisateurs - par exemple, les identifiants de ressources tels que les ID ou les actions / vues
  2. Filtres - Par exemple, des paramètres qui permettent de rechercher, de trier ou d'affiner l'ensemble des résultats.
  3. État - Par exemple, identification de session, clés api, whatevs.
  4. Contenu - Par exemple, les données à stocker.

Voyons maintenant les différents endroits où ces paramètres pourraient aller.

  1. Demander des en-têtes et des cookies
  2. Chaîne de requête URL (vars "GET")
  3. Chemins d'URL
  4. Chaîne de requête de corps / en plusieurs parties (vars "POST")

En règle générale, vous souhaitez que l'état soit défini dans les en-têtes ou les cookies, selon le type d'informations d'état qu'il s'agit. Je pense que nous pouvons tous être d'accord là-dessus. Utilisez des en-têtes http personnalisés (X-My-Header) si vous en avez besoin.

De même, le contenu n'a qu'un seul endroit à appartenir, qui se trouve dans le corps de la demande, soit en tant que chaînes de requête, soit en tant que contenu http en plusieurs parties et / ou JSON. Cela correspond à ce que vous recevez du serveur lorsqu'il vous envoie du contenu. Il ne faut donc pas être impoli et le faire différemment.

Des localisateurs tels que "id = 5" ou "action = refresh" ou "page = 2" auraient un sens à avoir comme chemin d'URL, par exemple mysite.com/article/5/page=2lorsque vous savez en partie ce que chaque partie est censée signifier (les bases telles que l'article et 5 signifie évidemment obtenir les données de type article avec l'ID 5) et des paramètres supplémentaires sont spécifiés dans le cadre de l'URI. Ils peuvent prendre la forme de page=2, ou page/2si vous savez qu'après un certain point dans l'URI, les "dossiers" sont des paires valeur / clé.

Les filtres vont toujours dans la chaîne de requête, car s'ils font partie de la recherche des bonnes données, ils ne sont là que pour renvoyer un sous-ensemble ou une modification de ce que les localisateurs retournent seuls. La recherche dans mysite.com/article/?query=Obama(sous-ensemble) est un filtre, tout comme /article/5?order=backwards(modification). Pensez à ce qu'il fait, pas seulement à son nom!

Si "view" détermine le format de sortie, alors c'est un filtre ( mysite.com/article/5?view=pdf) car il retourne une modification de la ressource trouvée plutôt que de trouver sur quelle ressource nous voulons. S'il décide à la place de la partie spécifique de l'article que nous voyons ( mysite.com/article/5/view=summary), il s'agit d'un localisateur.

N'oubliez pas que le rétrécissement d' un ensemble de ressources est un filtrage. Localiser quelque chose de spécifique dans une ressource, c'est localiser ... duh. Le filtrage de sous-ensemble peut renvoyer n'importe quel nombre de résultats (même 0). La localisation trouvera toujours cette instance spécifique de quelque chose (si elle existe). Le filtrage des modifications renvoie les mêmes données que le localisateur, sauf celles modifiées (si une telle modification est autorisée).

J'espère que cela a aidé les gens à vivre des moments eureka s'ils ont perdu où mettre des choses!

Tor Valamo
la source
2
Pourquoi n'est-il pas idun filtre alors? Il renvoie un sous-ensemble de la ressource
Jonathan.
13
@Jonathan. non, il renvoie une ressource spécifique, à savoir l'article numéro 5. Un filtre est toujours un moyen d'affiner une recherche dans une collection de ressources. Si vous voulez juste cette ressource spécifique, alors il devrait y avoir un moyen désigné pour l'obtenir. Le filtrage signifie que vous avez la possibilité de renvoyer plusieurs ressources. Un ID n'est pas un filtre, c'est une ressource unique définie. Si vous aviez une GAMME d'ID, alors ce serait un filtre, même si la plage ne comprenait qu'un ID. Si le filtre incluait également des types de ressources, il retournerait toutes les ressources avec l'ID 5, pas seulement l'article.
Tor Valamo
1
@Jonathan.: Comme DarrelMiller l'a mentionné, vous vous attendriez à ce qu'une demande d'objet / id renvoie 404 en cas d'ID inconnu, tandis que vous vous attendriez à ce que l'objet? Id = id revienne et vide la liste. En outre, je considérerais que tout type de filtrage / sous-ensemble devrait renvoyer une liste.
njzk2
1
Les pages sont difficiles, car comme vous le dites, elles peuvent être le filtre d'une ressource (collection de pages), mais en même temps, c'est une ressource spécifique au sein de cette collection. Je demanderais toujours une page d'article par localisateur, pas par filtre. Cependant, la page peut être un filtre d'une liste de quelque chose, disons une liste d'utilisateurs. Mais alors la page est intrinsèquement un délimiteur, alias "commencer à l'élément (page-1)*perpageet afficher les perpageéléments". L'utiliser comme filtre est alors correct, mais pour des raisons différentes. L'appeler "page" est techniquement incorrect. Plus sémantiquement correct serait de l'appeler "de" ou "startAt"
Tor Valamo
1
(suite) Le sens sémantique de «page» est qu'il s'agit d'une ressource spécifique qui ne change pas. Cela vient de l'impression physique. Si nous n'avions jamais de livres ou d'imprimés, "page" ne serait pas vraiment un mot. Si vous avez une liste dynamique d'éléments, divisée en "pages", vous devez vraiment fournir un point de départ spécifique, soit numérique, alphabétique ou même spécifique à l'élément, ainsi qu'un filtre "combien par page". Si je veux faire référence à quelque chose dans votre liste, je veux des détails. De plus, je ne veux pas aller à la page 5 seulement pour réaliser que vous avez maintenant changé l'interne perpageen 50 au lieu de 20.
Tor Valamo
21

Cela dépend d'un design. Il n'y a pas de règles pour les URI à REST sur HTTP (l'essentiel est qu'ils sont uniques). Souvent, il s'agit de goût et d'intuition ...

J'adopte l'approche suivante:

  • url path-element: La ressource et son chemin-élément forment une traversée de répertoire et une sous-ressource (par exemple / items / {id}, / users / items). En cas de doute, demandez à vos collègues, s'ils pensent que la traversée et qu'ils pensent dans "un autre répertoire" l'élément de chemin le plus probable est le bon choix
  • paramètre url: quand il n'y a pas vraiment de traversée (les ressources de recherche avec plusieurs paramètres de requête en sont un très bel exemple)
manuel aldana
la source
1
Il existe en fait des règles assez claires sur l'apparence d'un URI et très peu d'ambiguïté sur la façon de les appliquer aux URI RESTful.
DanMan
18

OMI, les paramètres devraient être meilleurs comme arguments de requête. L'URL est utilisée pour identifier la ressource, tandis que les paramètres de requête ajoutés pour spécifier quelle partie de la ressource vous voulez, tout état que la ressource doit avoir, etc.

PeterWong
la source
7
En fait, le chemin d'accès et la requête sont utilisés en combinaison pour identifier la ressource. Cela a été clarifié dans la RFC 3986 http://labs.apache.org/webarch/uri/rfc/rfc3986.html#query
Darrel Miller
@DarrelMiller Je sais que c'est un ancien article, mais je suis intéressé à en savoir plus sur le fait que les paramètres de requête sont également utilisés pour identifier la ressource. Le lien que vous avez fourni est maintenant mort. J'ai regardé la RFC3986 mais je ne vois pas comment vous avez déduit ce fait. De plus, par définition, les paramètres d'un identifiant ne doivent pas être facultatifs, il ne semble donc pas approprié d'utiliser des paramètres de requête pour l'identification.
Mickael Marrache
@MickaelMarrache Voir la première ligne de la section 3.4 tools.ietf.org/html/rfc3986#section-3.4
Darrel Miller
2
@DarrelMiller Merci! Ma question vient du fait que généralement, les composants HTTP intermédiaires ne mettent pas en cache les réponses des requêtes qui contiennent une chaîne de requête. Il semble donc que les paramètres de requête soient plus appropriés pour rechercher des ressources selon certains critères et non pour identifier de manière unique une ressource.
Mickael Marrache
17

Selon l'implémentation REST,

1) Les variables de chemin sont utilisées pour l'action directe sur les ressources, comme un contact ou une chanson par exemple.
GET etc / api / resource / {songid} ou
GET etc / api / resource / {contactid} renverra les données respectives.

2) Les perms / arguments de requête sont utilisés pour les ressources indirectes comme les métadonnées d'une chanson ex .., GET / api / resource / {songid}? Metadata = genres, il renverra les données de genre pour cette chanson particulière.

Satish Bellapu
la source
5
Il n'y a en fait pas de norme REST . Par Wikipedia : Contrairement aux services Web basés sur SOAP, il n'y a pas de norme "officielle" pour les API Web RESTful. [14] En effet, REST est un style architectural, contrairement à SOAP, qui est un protocole. Même si REST n'est pas une norme, une implémentation RESTful telle que le Web peut utiliser des normes comme HTTP, URI, XML, etc.
DavidRR
Je n'aime pas l'approche 2. Je préfère / api / genres? Songid = 123 ou / api / songs / {song-id} / genres
Bart Calixto
1
@Bart, Satish faisait référence à des variables dans le chemin d'accès, ce qui est essentiellement ce que vous avez appelé votre préférence .. cependant, si les genres sont en fait des métadonnées, et non un champ de l'entité / ressource de la chanson .. en utilisant une chaîne de requête dessus.
Brett Caswell
@BrettCaswell l'a compris! merci de l'avoir signalé. j'apprécie beaucoup!
Bart Calixto
16

"Emballez" et POSTEZ vos données dans le "contexte" fourni par l'univers-resource-locator, ce qui signifie # 1 pour le localisateur.

Attention aux limitations avec # 2. Je préfère les POST au # 1.

note: les limitations sont discutées pour

POST dans Existe-t-il une taille maximale pour le contenu des paramètres POST?

GET in Y a-t-il une limite à la durée d'une demande GET? et taille maximale des paramètres d'URL dans _GET

ps ces limites sont basées sur les capacités du client (navigateur) et du serveur (configuration).

dgm
la source
module complémentaire: les itinéraires pleins d'esprit peuvent avoir des versions (distinguées via des en-têtes) fournissent ainsi des fonctionnalités évoluées sans avoir besoin de modifier le code qui consomme le code de repos complet (api) que vous écrivez comme dans restify -> recherchez les itinéraires versionnés
dgm
5

Selon la norme URI, le chemin est pour les paramètres hiérarchiques et la requête pour les paramètres non hiérarchiques. Ofc. cela peut être très subjectif ce qui est hiérarchique pour vous.

Dans les situations où plusieurs URI sont attribués à la même ressource, j'aime mettre les paramètres - nécessaires à l'identification - dans le chemin et les paramètres - nécessaires pour construire la représentation - dans la requête. (Pour moi, de cette façon, il est plus facile de router.)

Par exemple:

  • /users/123 et /users/123?fields="name, age"
  • /users et /users?name="John"&age=30

Pour réduire la carte, j'aime utiliser les approches suivantes:

  • /users?name="John"&age=30
  • /users/name:John/age:30

C'est donc à vous (et à votre routeur côté serveur) de décider comment vous construisez vos URI.

remarque: juste pour mentionner que ces paramètres sont des paramètres de requête. Donc, ce que vous faites vraiment, c'est définir un langage de requête simple. Par des requêtes complexes (qui contiennent des opérateurs comme et, ou, supérieur à, etc.), je vous suggère d'utiliser un langage de requête déjà existant. Les capacités des modèles d'URI sont très limitées ...

inf3rno
la source
4

En tant que programmeur souvent côté client, je préfère l'argument de requête. De plus, pour moi, il sépare le chemin URL des paramètres, ajoute à la clarté et offre plus d'extensibilité. Cela me permet également d'avoir une logique distincte entre le bâtiment URL / URI et le générateur de paramètres.

J'aime ce que Manuel Aldana a dit à propos de l'autre option s'il y a une sorte d'arbre impliqué. Je peux voir des parties spécifiques à l'utilisateur être tracées comme ça.

Joe Plante
la source
4

Il n'y a pas de règles strictes et rapides, mais la règle empirique d'un point de vue purement conceptuel que j'aime utiliser peut être brièvement résumée comme ceci: un chemin URI (par définition) représente une ressource et les paramètres de requête sont essentiellement des modificateurs de cette ressource . Jusqu'à présent , qui ne risque pas d' aide ... Avec une API REST vous avez les principales méthodes d'agir sur une seule ressource en utilisant GET, PUTet DELETE. Par conséquent, le fait de savoir si quelque chose doit être représenté dans le chemin ou en tant que paramètre peut être réduit à la pertinence de ces méthodes pour la représentation en question. Souhaitez-vous raisonnablement PUTquelque chose sur ce chemin et serait-il sémantique de le faire? Vous pouvez bien sûr PUTquelque chose à peu près n'importe où et plier le back-end pour le gérer, mais vous devriez êtrePUTce qui équivaut à une représentation de la ressource réelle et non à une version inutilement contextualisée de celle-ci. Pour les collections, la même chose peut être faite avec POST. Si vous vouliez ajouter à une collection particulière quelle serait une URL qui aurait du sens POST.

Cela laisse encore des zones d'ombre car certains chemins pourraient indiquer le montant pour les enfants des ressources parentales, ce qui est quelque peu discrétionnaire et dépendant de leur utilisation. La seule ligne dure que cela trace est que tout type de représentation transitive doit être effectuée à l'aide d'un paramètre de requête, car il n'aurait pas de ressource sous-jacente.

En réponse à l'exemple du monde réel donné dans la question d'origine (API de Twitter), les paramètres représentent une requête transitive qui filtre sur l'état des ressources (plutôt qu'une hiérarchie). Dans cet exemple particulier, il serait tout à fait déraisonnable d'ajouter à la collection représentée par ces contraintes, et en outre, cette requête ne pourrait pas être représentée comme un chemin qui aurait un sens dans les termes d'un graphique d'objet.

L'adoption de ce type de perspective orientée ressources peut facilement correspondre directement au graphique d'objet de votre modèle de domaine et conduire la logique de votre API au point où tout fonctionne très proprement et de manière assez auto-documentée une fois qu'il devient clair. Le concept peut également être rendu plus clair en s'éloignant des systèmes qui utilisent le routage d'URL traditionnel mappé sur un modèle de données normalement mal adapté (c'est-à-dire un SGBDR). Apache Sling serait certainement un bon point de départ. Le concept de répartition de la traversée d'objets dans un système comme Zope fournit également un analogue plus clair.

Matt Whipple
la source
4

Voici mon avis.

Les paramètres de requête sont utilisés comme métadonnées d'une demande. Ils agissent comme filtre ou modificateur d'un appel de ressource existant.

Exemple:

/calendar/2014-08-08/events

devrait donner des événements de calendrier pour ce jour.

Si vous souhaitez des événements pour une catégorie spécifique

/calendar/2014-08-08/events?category=appointments

ou si vous avez besoin d'événements de plus de 30 minutes

/calendar/2014-08-08/events?duration=30

Un test décisif serait de vérifier si la demande peut toujours être servie sans paramètres de requête.

Geai
la source
2

Je tends généralement vers # 2, comme argument de requête (ie / api / resource? Parameter = value).

Une troisième option consiste à afficher réellement le paramètre = valeur dans le corps.

En effet, il fonctionne mieux pour les ressources multi-paramètres et est plus extensible pour une utilisation future.

Peu importe celui que vous choisissez, assurez-vous de n'en choisir qu'un, de ne pas mélanger et assortir. Cela conduit à une API déroutante.

NorthIsUp
la source
2

Une "dimension" de ce sujet a été laissée de côté, mais elle est très importante: il y a des moments où les "meilleures pratiques" doivent être en accord avec la plateforme que nous mettons en œuvre ou augmentons avec les capacités REST.

Exemple pratique:

De nombreuses applications web implémentent aujourd'hui l'architecture MVC (Model, View, Controller). Ils supposent qu'un certain chemin standard est fourni, d'autant plus lorsque ces applications Web sont livrées avec une option "Activer les URL SEO".

Pour ne citer qu'une application web assez célèbre: une boutique e-commerce OpenCart. Lorsque l'administrateur active les «URL SEO», il s'attend à ce que ces URL soient fournies dans un format MVC assez standard comme:

http://www.domain.tld/special-offers/list-all?limit=25

  • special-offers est le contrôleur MVC qui doit traiter l'URL (montrant la page des offres spéciales)

  • list-allest le nom de l'action ou de la fonction du contrôleur à appeler. (*)

  • limit = 25 est une option, indiquant que 25 éléments seront affichés par page.

(*) list-allest un nom de fonction fictif que j'ai utilisé pour plus de clarté. En réalité, OpenCart et la plupart des frameworks MVC ont une fonction implicite (et généralement omise dans l'URL) indexpar défaut qui est appelée lorsque l'utilisateur souhaite qu'une action par défaut soit effectuée. L'URL du monde réel serait donc:

http://www.domain.tld/special-offers?limit=25

Avec une application ou une structure frameworkd désormais assez standard similaire à celle ci-dessus, vous obtiendrez souvent un serveur Web qui est optimisé pour cela, qui réécrit les URL pour cela (la vraie "URL non SEOed" serait:) http://www.domain.tld/index.php?route=special-offers/list-all&limit=25.

Par conséquent, en tant que développeur, vous devez gérer l'infrastructure existante et adapter vos "meilleures pratiques", sauf si vous êtes l'administrateur système, savez exactement comment modifier une configuration de réécriture Apache / NGinx (cette dernière peut être désagréable!), Etc. sur.

Ainsi, votre API REST serait souvent bien meilleure en suivant les normes de l'application Web référente, à la fois pour la cohérence avec elle et la facilité / vitesse (et donc l'économie de budget).

Pour revenir à l'exemple pratique ci-dessus, une API REST cohérente serait quelque chose avec des URL comme:

http://www.domain.tld/api/special-offers-list?from=15&limit=25

ou (URL non SEO)

http://www.domain.tld/index.php?route=api/special-offers-list?from=15&limit=25

avec un mélange d'arguments "chemins formés" et d'arguments "requête formés".

Dario Fumagalli
la source
1

Je vois beaucoup d'API REST qui ne gèrent pas bien les paramètres. Un exemple qui revient souvent est lorsque l'URI comprend des informations personnellement identifiables.

http://software.danielwatrous.com/design-principles-for-rest-apis/

Je pense qu'une question corollaire est de savoir quand un paramètre ne devrait pas du tout être un paramètre, mais devrait plutôt être déplacé vers l'en- tête ou le corps de la demande.

Daniel Watrous
la source
0

C'est une question très intéressante.

Vous pouvez les utiliser tous les deux, il n'y a pas de règle stricte à ce sujet, mais l'utilisation de variables de chemin URI présente certains avantages:

  • Cache : la plupart des services de cache Web sur Internet ne mettent pas en cache la requête GET lorsqu'ils contiennent des paramètres de requête. Ils le font parce qu'il y a beaucoup de systèmes RPC qui utilisent des requêtes GET pour changer les données sur le serveur (échec !! Get doit être une méthode sûre)

Mais si vous utilisez des variables de chemin, tous ces services peuvent mettre en cache vos demandes GET.

  • Hiérarchie : les variables de chemin peuvent représenter la hiérarchie: / Ville / Rue / Lieu

Il donne à l'utilisateur plus d'informations sur la structure des données.

Mais si vos données n'ont pas de relation hiérarchique, vous pouvez toujours utiliser des variables Path, en utilisant des virgules ou des points-virgules:

/ Ville / longitude, latitude

En règle générale, utilisez une virgule lorsque l'ordre des paramètres est important, utilisez le point-virgule lorsque l'ordre n'a pas d'importance:

/ IconGenerator / rouge; bleu; vert

En dehors de ces raisons, il existe des cas où il est très courant d'utiliser des variables de chaîne de requête:

  • Lorsque vous avez besoin du navigateur pour mettre automatiquement des variables de formulaire HTML dans l'URI
  • Lorsque vous traitez avec un algorithme. Par exemple, le moteur Google utilise des chaînes de requête:

http: // www.google.com/search?q=rest

Pour résumer, il n'y a aucune raison valable d'utiliser l'une de ces méthodes, mais chaque fois que vous le pouvez, utilisez des variables URI.

jfcorugedo
la source