Lors de la conception d'un service Web RESTful à l'aide de HATEOAS, quels sont les avantages et les inconvénients d'afficher un lien sous la forme d'une URL complète (" http: // serveur: port/application/clients/1234 ") par rapport à simplement le chemin ("/ application/clients/1234")?
Il y a une ambiguïté conceptuelle subtile lorsque les gens disent "URI relative".
Par définition RFC3986 , un URI générique contient:
URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
hier-part = "//" authority path-abempty
/ path-absolute
/ path-rootless
/ path-empty
foo://example.com:8042/over/there?name=ferret#nose
\_/ \______________/\_________/ \_________/ \__/
| | | | |
scheme authority path query fragment
La chose délicate est que, lorsque le schéma et l'autorité sont omis, la partie "chemin" elle-même peut être soit un chemin absolu (commence par /
) ou un chemin relatif "sans racine". Exemples:
"http://example.com:8042/over/there?name=ferret"
/over/there
here
ou ./here
ou ../here
ou etc.Donc, si la question était "si un serveur devrait produire chemin relatif en réponse reposante", la réponse est "Non" et le la raison détaillée est disponible ici . Je pense que la plupart des gens (dont moi) contre "URI relatif" sont en fait contre "chemin relatif".
Et en pratique, la plupart des frameworks MVC côté serveur peuvent facilement générer URI relatif avec chemin absolu comme /absolute/path/to/the/controller
, et la question devient "si l'implémentation du serveur doit préfixer un scheme://hostname:port
devant le chemin absolu ". Comme la question du PO. Je ne suis pas sûr de celui-ci.
D'une part, je pense toujours que le serveur renvoyant un uri complet est recommandé. Cependant, le serveur doit ne jamais coder en dur le hostname:port
chose à l'intérieur du code source comme ça (sinon je préfère revenir à l'uri relatif avec un chemin absolu). La solution est côté serveur obtenant toujours ce préfixe à partir de l'en-tête "Host" de la requête HTTP. Je ne sais pas si cela fonctionne pour toutes les situations.
En revanche, il ne semble pas très gênant pour le client de concaténer le http://example.com:8042
et le chemin absolu. Après tout, le client connaît déjà ce schéma et ce nom de domaine lorsqu'il envoie la demande au serveur, n'est-ce pas?
Dans l'ensemble, je dirais que recommande d'utiliser l'URI absolu, peut-être de revenir à l'URI relatif avec un chemin absolu, ne jamais utiliser de chemin relatif .
Cela dépend de qui écrit le code client. Si vous écrivez le client et le serveur, cela ne fait pas beaucoup de différence. Vous subirez soit la peine de créer les URL sur le client ou sur le serveur.
Cependant, si vous construisez le serveur et que vous vous attendez à ce que d'autres personnes écrivent du code client, elles vous aimeront beaucoup plus si vous fournissez des URI complets. La résolution des URI relatifs peut être un peu délicate. Tout d'abord, la façon dont vous les résolvez dépend du type de support renvoyé. Html a la balise de base, Xml peut avoir des balises xml: base dans chaque élément imbriqué, Atom peuvent avoir une base dans le flux et une base différente dans le contenu. Si vous ne fournissez pas votre client avec des informations explicites sur l'URI de base, puis il doit obtenir l'URI de base de l'URI de demande, ou peut-être de l'en-tête Content-Location! Et faites attention à cette barre oblique de fin. L'URI de base est déterminé en ignorant tous les caractères à droite de la dernière barre oblique. Cela signifie que la barre oblique de fin est désormais très importante lors de la résolution des URI relatifs.
Le seul autre problème qui nécessite une petite mention est la taille du document. Si vous renvoyez une grande liste d'éléments où chaque élément peut avoir plusieurs liens, l'utilisation d'URL absolues peut ajouter une quantité importante d'octets à votre entité si vous ne compressez pas l'entité. Il s'agit d'un problème de performance et vous devez décider s'il est significatif au cas par cas.
La seule vraie différence semble être qu'il est plus facile pour les clients s'ils consomment des URI absolus au lieu d'avoir à les construire à partir de la version relative. Bien sûr, cette différence serait suffisante pour me convaincre de faire la version absolue.
À mesure que votre application évolue, vous souhaiterez peut-être effectuer un équilibrage de charge, un basculement, etc. Si vous renvoyez des URI absolus, vos applications côté client suivront votre configuration évolutive de serveurs.
En utilisant la trichotomie de RayLo mon organisation a choisi de privilégier (2). La raison principale est d'éviter les attaques XSS (Cross-Site Scripting). Le problème est que si un attaquant peut injecter sa propre racine d'URL dans la réponse en provenance du serveur, les demandes d'utilisateurs suivantes (telles qu'une demande d'authentification avec nom d'utilisateur et mot de passe) peuvent être transmises au propre serveur de l'attaquant *.
Certains ont soulevé la question de pouvoir rediriger les demandes vers d'autres serveurs pour l'équilibrage de charge, mais (même si ce n'est pas mon domaine d'expertise) je parierais qu'il existe de meilleures façons d'activer l'équilibrage de charge sans avoir à rediriger explicitement les clients vers différents hôtes.
* veuillez me faire savoir s'il y a des défauts dans cette ligne de raisonnement. Le but, bien sûr, n'est pas d'empêcher toutes les attaques, mais au moins une voie d'attaque.
Vous devez toujours utiliser l'URL complète. Il agit comme l'identifiant unique de la ressource, car les URL doivent toutes être uniques.
Je dirais également que vous devriez être cohérent. Étant donné que l'en-tête HTTP Location attend une URL complète basée sur la spécification HTTP, l'URL complète est renvoyée dans l'en-tête Location au client lorsqu'une nouvelle ressource est créée. Il serait étrange pour vous de fournir une URL complète dans l'en-tête Location, puis des URI relatifs dans les liens de votre corps de réponse.
Un inconvénient de l'utilisation d'URI absolus est que l'api ne peut pas être mandatée.
Reprenez ... pas vrai. Vous devez opter pour une URL complète, y compris le domaine.
Concernant les pros, je constate la réduction des octets à transmettre au détriment du traitement supplémentaire requis par un client pour le chemin (absolu). Si vous êtes désespéré de sauvegarder chaque octet, même après avoir essayé le codage de contenu en tant que gzip, l'utilisation correcte des en-têtes de mise en cache, l'utilisation des étiquettes et des demandes conditionnelles sur le client, cela peut être nécessaire à la fin, mais je m'attends à des retours beaucoup plus élevés sur vos efforts étaient ailleurs.
En ce qui concerne les inconvénients, je constate une perte de contrôle sur la façon dont vous pouvez diriger le flux de clients entre les ressources à l'avenir (équilibrage de charge, tests A/B, ...), et je considère que c'est une mauvaise pratique concernant la gestion d'un site Web API. L'URL que vous fournissez n'est plus fondamentalement opaque pour le client (voir Tim Berners-Lee Axioms of Web Architecture on URI opacity ). En fin de compte, vous devenez responsable de garder les clients satisfaits de leur utilisation créative de votre API, même si cela ne concerne que la structure de votre espace URL. Si vous devez autoriser une modification d'URL explicitement définie, envisagez l'utilisation de modèles d'URI comme utilisé dans Hypertext Application Language .
Une considération importante dans les résultats d'API volumineux est la surcharge de réseau supplémentaire d'inclure l'URI complet à plusieurs reprises. Croyez-le ou non, gzip ne résout pas entièrement ce problème (vous ne savez pas pourquoi). Nous avons été choqués de voir combien d'espace l'URI complet a pris alors qu'il y avait des centaines de liens inclus dans un résultat.