Meilleures pratiques pour le contrôle de version d'API? [fermé]

877

Existe-t-il des procédures ou des meilleures pratiques connues pour le contrôle de version de l'API REST de service Web?

J'ai remarqué qu'AWS effectue le versionnement par l'URL du point de terminaison . Est-ce la seule façon ou existe-t-il d'autres façons d'atteindre le même objectif? S'il y a plusieurs façons, quels sont les avantages de chaque façon?

Swaroop CH
la source

Réponses:

682

C'est une bonne et une question délicate. Le sujet de la conception d'URI est en même temps la partie la plus importante d'une API REST et , par conséquent, un engagement potentiellement à long terme envers les utilisateurs de cette API .

Étant donné que l'évolution d'une application et, dans une moindre mesure, son API est une réalité et qu'elle est même similaire à l'évolution d'un produit apparemment complexe comme un langage de programmation, la conception de l' URI devrait avoir moins de contraintes naturelles et elle devrait être préservée au fil du temps . Plus la durée de vie de l'application et de l'API est longue, plus l'engagement envers les utilisateurs de l'application et de l'API est important.

D'un autre côté, une autre réalité est qu'il est difficile de prévoir toutes les ressources et leurs aspects qui seraient consommés via l'API. Heureusement, il n'est pas nécessaire de concevoir l'intégralité de l'API qui sera utilisée jusqu'à Apocalypse . Il suffit de définir correctement tous les points d'extrémité de ressource et le schéma d'adressage de chaque ressource et instance de ressource.

Au fil du temps, vous devrez peut-être ajouter de nouvelles ressources et de nouveaux attributs à chaque ressource particulière, mais la méthode que les utilisateurs d'API suivent pour accéder à des ressources particulières ne devrait pas changer une fois qu'un schéma d'adressage de ressource devient public et donc définitif.

Cette méthode s'applique à la sémantique des verbes HTTP (par exemple, PUT doit toujours mettre à jour / remplacer) et aux codes d'état HTTP qui sont pris en charge dans les versions d'API antérieures (ils doivent continuer à fonctionner pour que les clients d'API qui ont travaillé sans intervention humaine puissent continuer à fonctionner). comme ça).

De plus, étant donné que l'intégration de la version de l'API dans l'URI perturberait le concept d' hypermédia en tant que moteur de l'état de l'application (indiqué dans la thèse de doctorat de Roy T. Fieldings) en ayant une adresse de ressource / URI qui changerait avec le temps, je conclurais que l' API les versions ne doivent pas être conservées pendant longtemps dans les URI de ressources, ce qui signifie que les URI de ressources sur lesquels les utilisateurs d'API peuvent compter doivent être des permaliens .

Bien sûr, il est possible d'incorporer la version d'API dans l'URI de base, mais uniquement pour des utilisations raisonnables et restreintes comme le débogage d'un client d'API qui fonctionne avec la nouvelle version d'API. Ces API versionnées devraient être limitées dans le temps et disponibles pour des groupes limités d'utilisateurs d'API (comme pendant les versions bêta fermées) uniquement. Sinon, vous vous engagez là où vous ne devriez pas.

Quelques réflexions sur la maintenance des versions d'API qui ont une date d'expiration. Toutes les plates-formes / langages de programmation couramment utilisés pour implémenter des services Web (Java, .NET, PHP, Perl, Rails, etc.) permettent de lier facilement les points d'extrémité de service Web à un URI de base. De cette façon, il est facile de rassembler et de conserver une collection de fichiers / classes / méthodes séparées entre les différentes versions d'API .

À partir du POV des utilisateurs d'API, il est également plus facile de travailler avec une version d'API particulière et de s'y lier lorsque cela est évident, mais uniquement pour une durée limitée, c'est-à-dire pendant le développement.

À partir du POV du mainteneur d'API, il est plus facile de maintenir différentes versions d'API en parallèle en utilisant des systèmes de contrôle de source qui fonctionnent principalement sur des fichiers comme la plus petite unité de version (code source).

Cependant, avec les versions d'API clairement visibles dans l'URI, il y a une mise en garde: on pourrait également s'opposer à cette approche car l' historique des API devient visible / apparenté dans la conception de l'URI et est donc sujet à des changements au fil du temps, ce qui va à l'encontre des directives de REST. Je suis d'accord!

La façon de contourner cette objection raisonnable est d'implémenter la dernière version d'API sous l'URI de base d'API sans version. Dans ce cas, les développeurs de clients API peuvent choisir:

  • développer contre la dernière (en s'engageant à maintenir l'application en la protégeant des éventuels changements d'API qui pourraient casser leur client d'API mal conçu ).

  • se lier à une version spécifique de l'API (qui devient apparente) mais uniquement pour une durée limitée

Par exemple, si l'API v3.0 est la dernière version d'API, les deux suivantes doivent être des alias (c'est-à-dire se comporter de manière identique pour toutes les demandes d'API):

http: // shonzilla / api / clients / 1234 
http: // shonzilla / api /v3.0 / clients / 1234
http: // shonzilla / api / v3 / customers / 1234

De plus, les clients d'API qui tentent toujours de pointer vers l' ancienne API doivent être informés de l'utilisation de la dernière version d'API précédente, si la version d'API qu'ils utilisent est obsolète ou n'est plus prise en charge . Donc, accéder à l'un des URI obsolètes comme ceux-ci:

http: // shonzilla / api /v2.2 / customers / 1234
http: // shonzilla / api /v2.0 / customers / 1234
http: // shonzilla / api / v2 / customers / 1234
http: // shonzilla / api /v1.1 / customers / 1234
http: // shonzilla / api / v1 / customers / 1234

doit renvoyer l'un des codes d'état HTTP 30x qui indiquent la redirection utilisée en conjonction avec l' Locationen-tête HTTP qui redirige vers la version appropriée de l'URI de ressource qui reste celle-ci:

http: // shonzilla / api / clients / 1234

Il existe au moins deux codes d'état HTTP de redirection appropriés pour les scénarios de version d'API:

  • 301 Déplacé de manière permanente indiquant que la ressource avec un URI demandé est déplacée de manière permanente vers un autre URI (qui doit être un permalien d'instance de ressource qui ne contient pas d'informations de version d'API). Ce code d'état peut être utilisé pour indiquer une version d'API obsolète / non prise en charge, informant le client API qu'un URI de ressource versionné a été remplacé par un permalien de ressource .

  • 302 Trouvé indiquant que la ressource demandée se trouve temporairement à un autre emplacement, alors que l'URI demandé peut toujours être pris en charge. Ce code d'état peut être utile lorsque les URI sans version sont temporairement indisponibles et qu'une demande doit être répétée en utilisant l'adresse de redirection (par exemple en pointant vers l'URI avec la version APi intégrée) et que nous voulons dire aux clients de continuer à l'utiliser (c'est-à-dire le permaliens).

  • d'autres scénarios peuvent être trouvés dans le chapitre Redirection 3xx de la spécification HTTP 1.1

Shonzilla
la source
142
L'utilisation d'un numéro de version dans l'URL ne doit pas être considérée comme une mauvaise pratique lorsque l'implémentation sous-jacente change. "Lorsque l'interface vers un service change de manière non rétrocompatible, en réalité un service entièrement nouveau a été créé ... Du point de vue du client, un service n'est plus qu'une interface et des qualités non fonctionnelles. .si l'interface avec un service change d'une manière non rétrocompatible, elle ne représente plus une instance du service d'origine, mais est plutôt un service complètement nouveau. " ibm.com/developerworks/webservices/library/ws-version
benvolioT
7
Avez-vous des idées sur l'ajout d'un en-tête avec le numéro de version afin qu'il puisse être vérifié par les clients ou les développeurs?
webclimber
11
Voir également l'utilisation d'un en-tête Accept pour indiquer la version que le client attend: blog.steveklabnik.com/2011/07/03/…
Weston Ruter
52
Pour la dernière partie: je dirais qu'une API obsolète et qui n'est plus prise en charge devrait revenir 410 Gone, car une redirection pourrait indiquer que le nouvel emplacement est compatible lorsqu'il ne l'est pas. Si l'API est simplement obsolète mais existe toujours, un Warningen-tête HTTP sur la réponse peut être une option.
Michael Stum
22
Comment traitez-vous avec les clients qui utilisent déjà l'URL stable comme shonzilla / api / customers / 1234 et vous souhaitez passer à une nouvelle version? comment pouvez-vous les forcer à ajouter le V2 (l'ancien) à l'URL?
Dejell
273

L'URL ne doit PAS contenir les versions. La version n'a rien à voir avec "l'idée" de la ressource que vous demandez. Vous devriez essayer de penser à l'URL comme étant un chemin vers le concept que vous souhaitez - pas comment vous voulez que l'article soit retourné. La version dicte la représentation de l'objet, pas le concept de l'objet. Comme d'autres affiches l'ont dit, vous devez spécifier le format (y compris la version) dans l'en-tête de la demande.

Si vous regardez la requête HTTP complète pour les URL qui ont des versions, cela ressemble à ceci:

(BAD WAY TO DO IT):

http://company.com/api/v3.0/customer/123
====>
GET v3.0/customer/123 HTTP/1.1
Accept: application/xml

<====
HTTP/1.1 200 OK
Content-Type: application/xml
<customer version="3.0">
  <name>Neil Armstrong</name>
</customer>

L'en-tête contient la ligne qui contient la représentation que vous demandez ("Accepter: application / xml"). C'est là que la version devrait aller. Tout le monde semble ignorer le fait que vous souhaitiez peut-être la même chose dans différents formats et que le client devrait pouvoir demander ce qu'il veut. Dans l'exemple ci-dessus, le client demande TOUTE représentation XML de la ressource - pas vraiment la vraie représentation de ce qu'il veut. Le serveur pourrait, en théorie, renvoyer quelque chose de complètement indépendant de la demande tant qu'il s'agissait de XML et il faudrait l'analyser pour réaliser qu'il est incorrect.

Une meilleure façon est:

(GOOD WAY TO DO IT)

http://company.com/api/customer/123
===>
GET /customer/123 HTTP/1.1
Accept: application/vnd.company.myapp.customer-v3+xml

<===
HTTP/1.1 200 OK
Content-Type: application/vnd.company.myapp-v3+xml
<customer>
  <name>Neil Armstrong</name>
</customer>

De plus, disons que les clients pensent que le XML est trop verbeux et qu'ils veulent maintenant JSON à la place. Dans les autres exemples, vous devrez avoir une nouvelle URL pour le même client, vous vous retrouverez donc avec:

(BAD)
http://company.com/api/JSONv3.0/customers/123
  or
http://company.com/api/v3.0/customers/123?format="JSON"

(ou quelque chose de similaire). Quand en fait, chaque requête HTTP contient le format que vous recherchez:

(GOOD WAY TO DO IT)
===>
GET /customer/123 HTTP/1.1
Accept: application/vnd.company.myapp.customer-v3+json

<===
HTTP/1.1 200 OK
Content-Type: application/vnd.company.myapp-v3+json

{"customer":
  {"name":"Neil Armstrong"}
}

En utilisant cette méthode, vous avez beaucoup plus de liberté dans la conception et adhérez en fait à l'idée originale de REST. Vous pouvez changer de version sans perturber les clients ou changer les clients de manière incrémentielle à mesure que les API sont modifiées. Si vous choisissez d'arrêter de prendre en charge une représentation, vous pouvez répondre aux demandes avec un code d'état HTTP ou des codes personnalisés. Le client peut également vérifier que la réponse est au bon format et valider le XML.

Il existe de nombreux autres avantages et j'en discute certains ici sur mon blog: http://thereisnorightway.blogspot.com/2011/02/versioning-and-types-in-resthttp-api.html

Un dernier exemple pour montrer comment mettre la version dans l'URL est mauvais. Disons que vous voulez des informations à l'intérieur de l'objet et que vous avez versionné vos différents objets (les clients sont v3.0, les commandes sont v2.0 et l'objet shipto est v4.2). Voici l'URL désagréable que vous devez fournir au client:

(Another reason why version in the URL sucks)
http://company.com/api/v3.0/customer/123/v2.0/orders/4321/
jeremyh
la source
10
La gestion des versions de contrat de données indépendantes et des versions de contrat de service dans l'en-tête Accept semble aussi compliquée que désordonnée dans l'URL. Il y a-t-il des alternatives ? De plus, si j'ai plusieurs points de terminaison (savon, repos), cela devrait-il également être indiqué dans Accepte et laisser le service de routage au niveau du serveur décider de la direction vers le point de terminaison correct OU est-il acceptable d'avoir le point de terminaison codé dans l'URL?
ideafountain
117
Je ne peux pas être d'accord avec cela, du moins au point de votre dernière raison. Cela semble dire que les différentes parties de l'URI ont des versions différentes. Mais ce n'est pas le point d'une version API. Le but est d'avoir UNE version pour la ressource ENTIÈRE. Si vous changez de version, il s'agit d'une ressource API différente. C'est pourquoi cela n'a pas de sens de voir company.com/api/v3.0/customer/123/v2.0/orders/4321 mais plutôt company.com/api/v3.0/customer/123/orders/4321 Vous ne versionnez pas une partie donnée de la ressource, vous versionnez la ressource dans son ensemble.
fool4jesus
90
L'utilisation sémantique du numéro de version dans l'en-tête semble meilleure. Mais il est beaucoup plus pratique d'utiliser l'URL: moins sujet aux erreurs, mieux débogué, facilement visible par les développeurs, facilement modifiable dans les tests de repos des clients.
Daniel Cerecedo
7
Je pense que le MAUVAIS / BON simplifie la question. API signifie "Application Programming Interface" et les interfaces de versioning semblent être une très bonne idée. Les API ne servent pas uniquement à servir des ressources. Ce qui doit être séparé, c'est que certaines personnes parlent d'interfaces et d'autres de ressources. Si vous regardez attentivement l'API Google Maps dans l'onglet Réseau, vous verrez qu'ils incluent le numéro de version de l'API dans l'URL. Par exemple: maps.google.com/maps/api/jsv2 lors de l'authentification. Le jsv2 est le numéro api.
Tom Gruner
6
@Gili: En fait, vous ne devriez plus l'utiliser -xcar il est déconseillé par RFC6648 .
Jonathan W
98

Nous avons trouvé pratique et utile de mettre la version dans l'URL. Il vous permet de savoir facilement ce que vous utilisez en un coup d'œil. Nous faisons des alias / foo à / foo / (dernières versions) pour la facilité d'utilisation, des URL plus courtes / plus propres, etc., comme le suggère la réponse acceptée.

Garder la compatibilité descendante pour toujours est souvent prohibitif et / ou très difficile. Nous préférons donner un avis avancé de dépréciation, des redirections comme suggéré ici, des documents et d'autres mécanismes.

Yoav Shapira
la source
5
La réponse acceptée peut être la bonne et la plus pure. Cependant, pour le développeur et l'utilisateur quotidien des API, c'est sûrement le plus facile à utiliser et à configurer. L'approche la plus pragmatique. Comme indiqué par d'autres Google et Amazon utilisent également cette approche.
Muhammad Rehan Saeed
46

Je suis d'accord que la version de la représentation des ressources suit mieux l'approche REST ... mais, un gros problème avec les types MIME personnalisés (ou les types MIME qui ajoutent un paramètre de version) est le mauvais support pour écrire dans les en-têtes Accept et Content-Type en HTML et JavaScript.

Par exemple, il n'est pas possible IMO de POST avec les en-têtes suivants dans les formulaires HTML5, afin de créer une ressource:

Accept: application/vnd.company.myapp-v3+json
Content-Type: application/vnd.company.myapp-v3+json 

En effet, l' enctypeattribut HTML5 est une énumération, donc autre chose que l'habituel application/x-www-formurlencoded, multipart/form-dataet text/plainn'est pas valide.

... et je ne suis pas sûr qu'il soit pris en charge par tous les navigateurs en HTML4 (qui a un attribut encytpe plus laxiste, mais serait un problème d'implémentation du navigateur quant à savoir si le type MIME a été transféré)

Pour cette raison, je pense maintenant que le moyen le plus approprié pour la version est via l'URI, mais j'accepte que ce ne soit pas la manière «correcte».

Kevsy
la source
14
En supposant l'itinéraire où le contrôle de version a été défini dans les en-têtes, on pourrait dire que les formulaires HTML qui utilisent la soumission de formulaire native utiliseraient toujours la dernière version de l'API car ils ne transmettraient pas la version spécifique à laquelle ils veulent adhérer. Cependant, les requêtes XHR vous permettent en effet de modifier les acceptations et de lire les en-têtes de type de contenu. Les formulaires de base sont donc vraiment le seul problème.
Kyle Hayes
Je ne suis pas sûr d'être d'accord que l'URI est le plus approprié, mais le fait que Content-Type ne fonctionne pas avec les formulaires est en effet très important.
wprl
2
@Kyle, j'ai vu un blog quelque part dire que si vous ne spécifiez pas de version dans l'en-tête de la demande, il est préférable de revenir avec la première version api et non la dernière pour la meilleure compatibilité.
andy
Cela fait beaucoup de sens pour moi maintenant que j'y pense.
Kyle Hayes
@KyleHayes n'oubliez pas les iframes, les tags de type video / embed et autres "src / href".
suppliez
21

Mettez votre version dans l'URI. Une version d'une API ne prend pas toujours en charge les types d'une autre, donc l'argument selon lequel les ressources sont simplement migrées d'une version à une autre est tout simplement faux. Ce n'est pas la même chose que de passer du format XML à JSON. Les types peuvent ne pas exister ou ils peuvent avoir changé sémantiquement.

Les versions font partie de l'adresse de ressource. Vous routez d'une API à une autre. Il n'est pas RESTful de masquer l'adressage dans l'en-tête.

Sean O'Dell
la source
13

Il y a quelques endroits où vous pouvez faire du versioning dans une API REST:

  1. Comme indiqué, dans l'URI. Cela peut être maniable et même esthétique si les redirections et similaires sont bien utilisées.

  2. Dans l'en-tête Accepts:, la version est donc dans le type de fichier. Comme «mp3» vs «mp4». Cela fonctionnera également, bien que l'OMI, cela fonctionne un peu moins bien que ...

  3. Dans la ressource elle-même. De nombreux formats de fichiers ont leur numéro de version intégré, généralement dans l'en-tête; cela permet aux logiciels plus récents de «fonctionner simplement» en comprenant toutes les versions existantes du type de fichier, tandis que les logiciels plus anciens peuvent se lancer si une version non prise en charge (plus récente) est spécifiée. Dans le contexte d'une API REST, cela signifie que vos URI ne doivent jamais changer, juste votre réponse à la version particulière des données qui vous a été remise.

Je peux voir les raisons d'utiliser les trois approches:

  1. si vous aimez faire de nouvelles API «clean sweep», ou pour des changements de version majeurs où vous voulez une telle approche.
  2. si vous voulez que le client sache avant de faire un PUT / POST si cela va fonctionner ou non.
  3. si ça va si le client doit faire son PUT / POST pour savoir si ça va marcher.
pjz
la source
8

Le contrôle de version de votre API REST est analogue au contrôle de version de toute autre API. Des changements mineurs peuvent être effectués sur place, des changements majeurs peuvent nécessiter une toute nouvelle API. Le plus simple pour vous est de recommencer à zéro à chaque fois, c'est quand il est plus logique de mettre la version dans l'URL. Si vous voulez simplifier la vie du client, vous essayez de maintenir la compatibilité descendante, ce que vous pouvez faire avec la dépréciation (redirection permanente), les ressources dans plusieurs versions, etc. Ceci est plus fastidieux et nécessite plus d'efforts. Mais c'est aussi ce que REST encourage dans «Les URI cool ne changent pas».

Au final, c'est comme n'importe quelle autre conception d'API. Pesez l'effort contre la commodité du client. Envisagez d'adopter le contrôle de version sémantique pour votre API, ce qui montre clairement à vos clients à quel point votre nouvelle version est rétrocompatible.

Alexander Torstling
la source