J'ai cherché comment gérer les versions d'une API REST en utilisant Spring 3.2.x, mais je n'ai rien trouvé de facile à maintenir. Je vais d'abord expliquer le problème que j'ai, puis une solution... mais je me demande si je ne suis pas en train de réinventer la roue.
Je veux gérer la version en fonction de l'en-tête Accept, et par exemple si une requête a l'en-tête Accept application/vnd.company.app-1.1+json
, je veux que Spring MVC redirige cela vers la méthode qui gère cette version. Et comme toutes les méthodes dans une API ne changent pas dans la même version, je ne veux pas aller dans chacun de mes contrôleurs et changer quoi que ce soit pour un gestionnaire qui n'a pas changé entre les versions. Je ne veux pas non plus avoir la logique pour déterminer quelle version utiliser dans les contrôleurs eux-mêmes (en utilisant des localisateurs de services) car Spring découvre déjà quelle méthode appeler.
Donc, en prenant une API avec des versions 1.0, à 1.8 où un gestionnaire a été introduit en version 1.0 et modifié en v1.7, je voudrais gérer cela de la manière suivante. Imaginez que le code soit à l'intérieur d'un contrôleur, et qu'il y ait du code capable d'extraire la version de l'en-tête. (Ce qui suit est invalide dans Spring)
@RequestMapping(...)
@VersionRange(1.0,1.6)
@ResponseBody
public Object method1() {
// so something
return object;
}
@RequestMapping(...) //même annotation de mappage de requête
@VersionRange(1.7)
@ResponseBody
public Object method2() {
// so something
return object;
}
Ce n'est pas possible dans Spring car les 2 méthodes ont la même annotation RequestMapping
et Spring échoue au chargement. L'idée est que l'annotation VersionRange
peut définir une plage de versions ouverte ou fermée. La première méthode est valide des versions 1.0 à 1.6, tandis que la seconde est pour la version 1.7 et suivantes (y compris la dernière version 1.8). Je sais que cette approche échoue si quelqu'un décide de passer la version 99.99, mais c'est quelque chose avec lequel je suis prêt à vivre.
Maintenant, comme ce qui précède n'est pas possible sans une refonte sérieuse de la façon dont Spring fonctionne, je pensais bricoler avec la manière dont les gestionnaires sont associés aux requêtes, en particulier pour écrire ma propre ProducesRequestCondition
, et avoir la plage de version là-dedans. Par exemple
Code:
@RequestMapping(..., produces = "application/vnd.company.app-[1.0-1.6]+json)
@ResponseBody
public Object method1() {
// so something
return object;
}
@RequestMapping(..., produces = "application/vnd.company.app-[1.7-]+json)
@ResponseBody
public Object method2() {
// so something
return object;
}
De cette façon, je peux avoir des plages de versions ouvertes ou fermées définies dans la partie produces de l'annotation. Je travaille sur cette solution maintenant, avec le problème que j'ai quand même dû remplacer certaines classes principales de Spring MVC (RequestMappingInfoHandlerMapping
, RequestMappingHandlerMapping
et RequestMappingInfo
), ce que je n'aime pas, car cela signifie un travail supplémentaire chaque fois que je décide de passer à une version plus récente de Spring.
Je serais reconnaissant pour toute réflexion... et surtout, toute suggestion pour le faire de manière plus simple, plus facile à maintenir.
Édition
Ajout d'une prime. Pour obtenir la prime, veuillez répondre à la question ci-dessus sans suggérer d'avoir cette logique dans les contrôleurs eux-mêmes. Spring a déjà beaucoup de logique pour sélectionner quelle méthode de contrôleur appeler, et je veux m'appuyer là-dessus.
Édition 2
J'ai partagé le POC original (avec quelques améliorations) sur Github : https://github.com/augusto/restVersioning
0 votes
stackoverflow.com/questions/11715874/…
1 votes
@flup Je ne comprends pas votre commentaire. Cela dit simplement que vous pouvez utiliser des en-têtes et, comme je l'ai dit, ce que Spring offre par défaut n'est pas suffisant pour prendre en charge des API constamment mises à jour. Pire encore, le lien de cette réponse utilise la version dans l'URL.
0 votes
Peut-être pas exactement ce que vous recherchez, mais Spring 3.2 prend en charge un paramètre "produces" sur RequestMapping. La seule exception est que la liste des versions doit être explicite. Par exemple,
produces={"application/json-1.0", "application/json-1.1"}
, etc.0 votes
@bimsapi oui, je connais ce paramètre. et tu m'as fait réaliser que j'ai commis une erreur dans la question. Étant donné que produces n'accepte pas une plage de versions, j'écris ma propre
ProducesRequestCondition
, qui peut comprendre des versions. Comme je l'ai mentionné ci-dessus, pour écrire cette classe, j'ai également dû réécrireRequestMappingInfoHandlerMapping
,RequestMappingHandlerMapping
etRequestMappingInfo
, carRequestMappingInfo
est final :(.0 votes
La question/problème me semble être un doublon, même si vous préférez une autre direction de solution. La réponse acceptée là-bas dit que vous pouvez utiliser des en-têtes, mais d'autres réponses utilisant le type de contenu répondraient aussi à la question.
0 votes
Il me semble que vous n'avez pas 9 versions de cette ressource, vous en avez seulement 2. Je dirais que si vous voulez versionner l'API (il semble que vous le vouliez), alors mettez la version dans l'URI. Si vous voulez versionner la ressource (ou sa représentation) à laquelle ce point de terminaison fait référence, mettez-la dans l'en-tête accepter. Je ne recommanderais pas de faire les deux, sinon ça va devenir compliqué (comme vous l'avez découvert).
1 votes
Nous devons prendre en charge plusieurs versions de nos API, ces différences sont généralement des changements mineurs qui rendraient certaines appels de certains clients incompatibles (il ne sera pas étrange si nous devons prendre en charge 4 versions mineures, dans lesquelles certains endpoints sont incompatibles). J'apprécie la suggestion de le mettre dans l'URL, mais nous savons que c'est une erreur, car nous avons quelques applications avec la version dans l'URL et il y a beaucoup de travail impliqué à chaque fois que nous devons augmenter la version.
1 votes
@Augusto, en fait, tu ne l'as pas fait non plus. Concevez simplement vos changements d'API d'une manière qui ne casse pas la compatibilité ascendante. Donnez-moi simplement un exemple de modifications qui cassent la compatibilité et je vous montrerai comment apporter ces modifications de manière non cassante.
1 votes
Avez-vous consulté stackoverflow.com/a/10336769/2615437 qui semble indiquer que votre affirmation "Ce n'est pas possible en spring car les 2 méthodes ont la même annotation RequestMapping et Spring échoue à charger." n'est pas totalement correcte ?
0 votes
@xworker Je n'ai pas vu ça. C'est une solution proche de ce que je fais actuellement (car j'ai besoin de ma propre condition personnalisée), mais ça a l'air beaucoup plus propre que ma mise en œuvre.
0 votes
Xwoker vs. xworker :-)
0 votes
Je suis d'accord avec Alexey sur ce point. Suivez les règles de versionnement sémantique et minimisez les changements de version majeure, et vous n'aurez pas à gérer finement les plages de version. Si vous devez apporter une série de modifications rétrocompatibles, vous ne gérez pas très bien votre système. Je sais que ce n'est pas la réponse que vous recherchez, mais parfois la réponse est de ne pas construire une échelle pour escalader le mur de 20 pieds au bout de la ruelle sombre, mais de faire demi-tour et de retourner à Main Street. :)
0 votes
github.com/augusto/restVersioning est trop difficile à déboguer, si une erreur se produit.