Meilleures pratiques pour la gestion des versions d’API?

Existe-t-il des procédures ou des pratiques recommandées pour la gestion des versions de l’API REST du service Web?

J’ai remarqué qu’AWS effectue la gestion des versions en fonction de l’URL du noeud final . Est-ce la seule façon ou y a-t-il d’autres moyens d’atteindre le même objective? S’il y a plusieurs façons, quels sont les mérites de chaque manière?

C’est une bonne et une question délicate. Le sujet de la conception d’URI est à la fois la partie la plus importante d’une API REST et , par conséquent, un engagement potentiel à long terme envers les utilisateurs de cette API .

Depuis 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 URI doit avoir des contraintes moins naturelles et doit ê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’autre part, il est difficile de prévoir toutes les ressources et tous les aspects qui seraient consommés via l’API. Heureusement, il n’est pas nécessaire de concevoir l’API complète 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 instance de ressource et de ressource.

Au fil du temps, vous devrez peut-être append de nouvelles ressources et de nouveaux atsortingbuts à chaque ressource particulière, mais la méthode suivie par les utilisateurs d’API pour accéder à une ressource particulière ne devrait plus être modifiée.

Cette méthode s’applique à la sémantique des verbes HTTP (par exemple, PUT doit toujours mettre à jour / remplacer) et les codes d’état HTTP pris en charge dans les versions précédentes (ils doivent continuer à fonctionner pour que les clients API comme ça).

De plus, l’intégration de la version d’API dans l’URI perturberait le concept d’ hypermédia en tant que moteur de l’état d’application (énoncé dans la thèse de doctorat de Roy T. Fieldings) en ayant une adresse / URI de ressource qui changerait avec le temps les versions ne doivent pas être conservées dans les URI de ressources pendant un long moment, ce qui signifie que les URI de ressources dont les utilisateurs d’API peuvent dépendre doivent être des liens permanents .

Bien sûr, il est possible d’incorporer la version de l’API dans l’URI de base, mais uniquement pour des utilisations raisonnables et restreintes, telles que le débogage d’un client API fonctionnant avec la nouvelle version de l’API. De telles APIs versionnées doivent être limitées dans le temps et disponibles pour des groupes limités d’utilisateurs d’API (comme pendant les bêtas fermés) uniquement. Sinon, vous vous engagez là où vous ne devriez pas.

Quelques reflections concernant 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 les services Web (Java, .NET, PHP, Perl, Rails, etc.) permettent de lier facilement les points de terminaison du service Web à un URI de base. De cette façon, il est facile de rassembler et de conserver une série de fichiers / classes / méthodes distincts dans les différentes versions de l’API .

À partir du POV des utilisateurs d’API, il est également plus facile de travailler et de se lier à une version d’API particulière lorsque c’est évident, mais seulement pour un temps limité, c’est-à-dire pendant le développement.

À partir du POV du responsable de l’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 les 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 objecter cette approche puisque l’ historique de l’API devient visible dans la conception de l’URI et est donc susceptible de changer avec le temps . Je suis d’accord!

La manière de contourner cette objection raisonnable consiste à implémenter la dernière version de l’API sous l’URI de base de l’API sans version. Dans ce cas, les développeurs de clients API peuvent choisir entre:

  • développer par rapport à la dernière (en s’engageant à maintenir l’application le protégeant des éventuelles modifications de l’API susceptibles de casser leur client API mal conçu ).

  • se lier à une version spécifique de l’API (qui devient apparente) mais seulement pour un temps limité

Par exemple, si API v3.0 est la dernière version de l’API, les deux suivantes doivent être des alias (par exemple, comportement identique à toutes les demandes d’API):

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

En outre, les clients API qui tentent toujours de pointer vers l’ ancienne API doivent être informés de la dernière version de l’API précédente, si la version de l’API utilisée 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 30 codes d’état HTTP indiquant la redirection utilisés avec l’en-tête HTTP Location qui redirige vers la version appropriée de l’URI de ressource qui rest celle-ci:

 http: // shonzilla / api / customers / 1234

Il existe au moins deux codes d’état HTTP de redirection qui conviennent aux scénarios de contrôle de version de l’API:

  • 301 Déplacé de manière permanente pour indiquer que la ressource avec un URI demandé est déplacée de manière permanente vers un autre URI (qui doit être un lien permanent d’instance de ressource qui ne contient pas d’informations de version de l’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’une adresse URI de ressource versionnée a été remplacée par un lien permanent de ressource .

  • 302 Indique que la ressource demandée est temporairement située à 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 incorporée) et que liens permanents).

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

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 considérer l’URL comme un chemin vers le concept que vous souhaitez – et non comme vous voulez que l’élément soit renvoyé. La version dicte la représentation de l’object, pas le concept de l’object. Comme d’autres affiches l’ont dit, vous devriez spécifier le format (y compris la version) dans l’en-tête de la requête.

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  Neil Armstrong  

L’en-tête contient la ligne contenant 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 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 représentation réelle de ce qu’il veut. Le serveur pourrait, en théorie, renvoyer quelque chose de complètement indépendant de la requête tant qu’il s’agirait de XML et il faudrait l’parsingr pour se rendre compte qu’il est faux.

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  Neil Armstrong  

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 devriez avoir une nouvelle URL pour le même client, vous devriez donc vous retrouver 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). Lorsque, 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 réellement à l'idée originale de REST. Vous pouvez modifier les versions sans interrompre les clients, ou modifier progressivement les clients à 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 y a beaucoup d'autres avantages et j'en discute 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'object, et que vous avez mis à jour vos divers objects (les clients sont v3.0, les commandes sont v2.0 et les objects shipto sont v4.2). Voici l'URL méchante que vous devez fournir dans le client:

 (Another reason why version in the URL sucks) http://company.com/api/v3.0/customer/123/v2.0/orders/4321/ 

Nous avons trouvé pratique et utile de mettre la version dans l’URL. Il est facile de savoir ce que vous utilisez en un coup d’œil. Nous faisons des alias / foo vers / 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.

Conserver une compatibilité descendante pour toujours est souvent coûteux et / ou très coûteux. Nous préférons signaler les dépréciations avancées, les redirections telles que suggérées ici, les documents et autres mécanismes.

Je suis d’accord que le versionnage 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 faible support pour écrire dans les en-têtes Accept et Content-Type en HTML. JavaScript

Par exemple, il n’est pas possible d’IMO pour 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’atsortingbut HTML5 enctype est une énumération. Par conséquent, tout autre élément que l’ application/x-www-formurlencoded , multipart/form-data et text/plain habituel n’est pas valide.

… je ne suis pas sûr non plus qu’il soit supporté par tous les navigateurs HTML4 (qui ont un atsortingbut encytpe plus laxiste, mais serait un problème d’implémentation du navigateur pour savoir si le type MIME a été transféré)

Pour cette raison, je pense maintenant que la manière la plus appropriée de la version est via l’URI, mais j’accepte que ce n’est pas la manière correcte.

Mettez votre version dans l’URI. Une version d’une API ne prend pas toujours en charge les types d’une autre, de sorte que 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 passer du format XML au format JSON. Les types peuvent ne pas exister ou ils peuvent avoir changé sémantiquement.

Les versions font partie de l’adresse de la ressource. Vous passez d’un API à un autre. Ce n’est pas RESTful de cacher l’adressage dans l’en-tête.

Il y a quelques endroits où vous pouvez faire de la gestion des versions dans une API REST:

  1. Comme noté, dans l’URI. Cela peut être facile et même esthétique si les redirections et autres sont bien utilisées.

  2. Dans l’en-tête Accepted, la version est dans le type de fichier. Comme ‘mp3’ vs ‘mp4’. Cela fonctionnera aussi, bien que IMO fonctionne un peu moins bien que …

  3. Dans la ressource elle-même De nombreux formats de fichiers ont leurs numéros de version intégrés, généralement dans l’en-tête; Cela permet aux nouveaux logiciels de «fonctionner» simplement en comprenant toutes les versions existantes du type de fichier, tandis que les anciens logiciels peuvent générer des erreurs 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 n’ont jamais à changer, mais uniquement votre réponse à la version de données que vous avez reçue.

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

  1. Si vous aimez faire de nouvelles API de balayage ou des modifications majeures de version où vous souhaitez une telle approche.
  2. Si vous voulez que le client sache avant de faire un PUT / POST, que cela fonctionne ou non.
  3. Si ça va si le client doit faire son PUT / POST pour savoir si ça va marcher.

Le contrôle de version de votre API REST est analogue au contrôle de version de toute autre API. Des modifications mineures peuvent être effectuées sur place, des modifications majeures pouvant nécessiter une toute nouvelle API. Le plus simple pour vous est de partir de zéro à chaque fois, ce qui est très utile lorsque vous insérez la version dans l’URL. Si vous voulez rendre la vie plus facile pour le client, essayez de maintenir une compatibilité ascendante, 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 URIs cool ne changent pas”.

Au final, c’est comme n’importe quelle autre conception d’API. Peser les efforts contre la commodité du client. Pensez à adopter le contrôle de version sémantique pour votre API, ce qui indique clairement à vos clients à quel point votre nouvelle version est rétrocompatible.