Comment gérer les parameters d’acceptation lors du développement d’une application jax-rs

Afin de traiter différentes versions d’un type de contenu, j’essaie d’utiliser les parameters accept des en-têtes “Accept *” ( RFC 2616 ).

Accept: application/vnd.mycompany.mytype;version=2 , application/vnd.mycompany.mytype;version=1;q=0.1 

Le problème est que les annotations Jax-RS ne supportent pas les parameters Accepter …

 @GET @Produces("application/vnd.test;version=1") public Response test1() { return Response.ok("Version 1", "application/vnd.test").build(); } @GET @Produces("application/vnd.test;version=2") public Response test2() { return Response.ok("Version 2", "application/vnd.test").build(); } 

Résultats dans une exception de conflit de type de média:

 Producing media type conflict. The resource methods public javax.ws.rs.core.Response test.resources.TestResource.test2() and public javax.ws.rs.core.Response test.resources.TestResource.test1() can produce the same media type 

Peut-être que cette exception ne concerne que mon framework JAX-RS (Jersey), mais je crains que cela ne soit dû au JSR311 qui n’est pas explicite à propos des parameters acceptés.

A présent, j’utilise des types de contenu qui contiennent la version dans leurs noms, mais j’ai trouvé cette solution plutôt moche.

 @GET @Produces("application/vnd.test-v1") public Response test() { return Response.ok("Version 1", "application/vnd.test-v1").build(); } 

Avez-vous des idées sur la façon de traiter les parameters d’acceptation?

MODIFIER

Je pense que je n’étais pas assez clair. Je souhaite acheminer automatiquement la demande à des méthodes spécifiques. Ces méthodes sont versionnées et correspondent à une version spécifique du type de contenu renvoyé. L’implémentation actuelle de JAX-RS m’empêche d’utiliser des parameters d’acceptation pour acheminer la demande (vers la méthode correspondante).

greenkode suggère que je gère le paramètre accept- version dans une méthode de dispatching (en utilisant @HeaderParam("Accept") ). Cette solution aboutirait à réécrire la logique de négociation de contenu qui est intégrée dans le cadre (et décrite dans JSR 311).

Que puis-je faire pour utiliser la logique de négociation de paramètre accepté et de contenu de JAX-RS?

Une solution consiste peut-être à utiliser un autre cadre (je n’ai travaillé avec Jersey que maintenant). Mais je ne sais pas lequel.

La spécification JAX-RS n’indique pas explicitement d’ignorer les parameters d’en-tête Accept. Mais le seul paramètre pour lequel le traitement est clairement défini est la qualité (q). C’est un domaine d’amélioration possible car il semble avoir entraîné une ambiguïté (ou une anomalie totale) dans la mise en œuvre de Jersey. La version actuelle de Jersey (1.17) ne prend pas en compte les parameters d’en-tête Accept lors de la mise en correspondance des demandes entrantes avec les méthodes de ressources. C’est pourquoi vous obtenez l’erreur suivante:

SEVERE: produisant un conflit de type de média. Les méthodes de ressources …

Pour la ressource:

 @GET @Produces("application/vnd.test;version=1") public Response test1() { return Response.ok("Version 1", "application/vnd.test").build(); } @GET @Produces("application/vnd.test;version=2") public Response test2() { return Response.ok("Version 2", "application/vnd.test").build(); } 

Il semblerait que Jersey effectue une vérification «d’unicité» basée sur l’en-tête Accepter ‘type / sous-type’, en omettant totalement les parameters. Cela peut être confirmé en testant avec différentes paires d’en-têtes sur les méthodes de ressources ‘correspondantes’:

 Ressource 1 Ressource 2
 --------------------------------------------
 text / html; q = 0,4 text / html; q = 0,8
 text / html text / html; q = 0,2
 text / html text / html; qs = 1.4
 text / html; qs = 1,4 text / html; qs = 1,8
 text / html; level = 1 text / html; level = 2
 text / html; foo = bleh text / html; bar = 23

Tous échouent avec la même erreur. Si l’on part du principe que seul le paramètre de qualité est envoyé, il serait alors logique de ne faire correspondre que le type / sous-type, car ce type de demande n’a aucun sens:

Accepter: text / html; q = 0,8, text / html; q = 0,4, text / html

Aka, les parameters de qualité n’ont de sens que lorsque vous traitez avec un mélange de types de contenu possibles. Cependant, ce type de correspondance limitée échoue lorsque des parameters de non qualité ou des parameters supplémentaires sont envoyés:

Accepter: text / html; version = 4.0; q = 0.8, text / html; version = 3.2; q = 0.4

Alors, quelles sont les solutions possibles?

  • Intercepter la requête de haut niveau basée sur ‘type / sous-type’, puis acheminer vers une méthode plus appropriée (vous avez indiqué que vous ne souhaitiez pas le faire)
  • Modifiez vos en-têtes attendus. Par exemple, “application / vnd.mycompany.mytype + v2” et “application / vnd.mycompany.mytype + v1”. Aucun autre changement ne serait nécessaire et vous pourriez continuer à utiliser Jersey.
  • Changer de cadre. RESTEasy arrive à gérer votre scénario avec facilité.

Avec RESTEasy, et ressource:

 @Path("/content/version") public class ContentVersionResource { @GET @Produces("application/vnd.test;version=1") public Response test1() { return Response.ok("Version 1", "application/vnd.test").build(); } @GET @Produces("application/vnd.test;version=2") public Response test2() { return Response.ok("Version 2", "application/vnd.test").build(); } } 

Une correspondance réussie est faite avec l’en-tête Accept suivant:

Accepter: application / vnd.test; version = 1; q = 0,3, application / vnd.test; version = 2; q = 0,5
Réponse: version 2

Et ça:

Accepter: application / vnd.test; version = 1; q = 0,5, application / vnd.test; version = 2; q = 0,3
Réponse: version 1

Vous pouvez télécharger et tester avec cet exemple de projet . Git, Maven et JBoss 7.x requirejs

À moins que je manque quelque chose. JAX-RS prend en charge les parameters d’acceptation. Regardez l’ @Consumes("*/*") . En outre, l’exception que vous obtenez avec un conflit de type de média se produit parce que vous avez deux méthodes GET à la même URL. @Path("test2") méthode test2 () avec @Path("test2") , puis envoyez votre demande GET à url / test2 à la place. cela devrait se débarrasser de cette erreur.

MODIFIER

Vous pouvez injecter la valeur de l’en-tête Accept aide de @HeaderParams . Voici un exemple de ce que j’ai fait.

 @Path("/conneg") public class ConnNeg { @GET @Produces("application/vnd.test;version=1") public Response test1(@HeaderParam("Accept") Ssortingng header) { System.out.println(header); return Response.ok("Version 1", "application/vnd.test").build(); } } 

en passant la demande

Accepter: application / vnd.test; version = 2, application / vnd.test; version = 1; q = 0,1

cela va imprimer

application / vnd.test; version = 2, application / vnd.test; version = 1; q = 0,1

Vous pouvez ensuite le gérer manuellement. Est-ce ce que vous cherchez?

Avec le framework Jersey, l’en-tête Accept de la requête HTTP a déclaré ce qui était le plus acceptable. Si une classe de ressources est capable de produire plus d’un type de support MIME, la méthode de ressource choisie correspond au type de support le plus acceptable déclaré par le client. Dans votre cas, si l’en-tête accept est

 Accept: application/vnd.mycompany.mytype;version=2 

alors la méthode test1 () sera invoquée.

Si c’est

 Accept: application/vnd.mycompany.mytype;q=0.9 version=2, application/vnd.mycompany.mytype;version=1 

ce dernier sera appelé.

Plusieurs types de média peuvent être déclarés dans la même déclaration @Produces, par exemple:

 @GET @Produces({"application/vnd.mycompany.mytype; version=2", "application/vnd.mycompany.mytype; version=1"}) public Response test() { return Response.ok("").build(); } 

le test (la méthode 9 sera appelée si l’un ou l’autre des 2 types de médiateurs est acceptable). Si les deux sont acceptables, le premier sera invoqué.

J’espère que cela aide!