在开发jax-rs应用程序时如何处理accept-parameters

为了处理内容类型的不同版本,我试图使用“Accept *”标头的接受参数( RFC 2616 )。

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

问题是Jax-RS注释不支持Accept-parameters …

 @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(); } 

导致媒体类型冲突exception:

 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 

也许,这个例外只与我的JAX-RS框架(Jersey)有关,但我担心这是由于JSR311没有明确的接受参数。

到目前为止,我正在使用内容类型,其中包含其名称中的版本,但我发现这个解决方案非常流畅。

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

您对如何处理接受参数有任何想法吗?

编辑

我想我还不够清楚。 我想自动将请求路由到特定方法。 这些方法是版本化的,并且对应于返回的内容类型的特定版本。 JAX-RS当前实现阻止我使用accept-parameters来路由请求(到相应的方法)。

greenkode建议我在调度方法中管理version accept-parameter(使用@HeaderParam("Accept") )。 该解决方案最终将重写内容解析逻辑,该逻辑嵌入框架中(并在JSR 311中描述)。

如何使用JAX-RS的accept-parameter和content-negociation逻辑呢?

也许解决方案是使用另一个框架(我现在只使用Jersey)。 但我不知道哪一个。

JAX-RS规范没有明确说明忽略Accept头参数的任何内容。 但唯一明确定义处理的参数是质量 (q)。 这是一个可能的改进领域,因为它似乎导致泽西岛实施中的歧义(或彻头彻尾的愚蠢)。 在将传入请求与资源方法匹配时,当前版本的Jersey(1.17)不会考虑Accept头参数,这就是您收到错误的原因:

严重:产生媒体类型冲突。 资源方法……

对于资源:

 @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(); } 

看起来Jersey会根据Accept标题’type / subtype’执行’唯一性’检查,完全省略任何参数。 这可以通过在“匹配”资源方法上使用各种标头对进行测试来确认:

资源1资源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

所有失败都有同样的错误。 如果假设只发送了质量参数,那么仅匹配’type / subtype’是有意义的,因为这种请求是荒谬的:

接受:text / html; q = 0.8,text / html; q = 0.4,text / html

Aka, 质量参数只有在处理各种可能的内容类型时才有意义。 但是,当发送非质量参数或其他参数时,这种有限匹配会失败:

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

那么可能的解决方案是什么?

  • 拦截基于’type / subtype’的高级请求,然后路由到更合适的方法(你已表明你不想这样做)
  • 修改您的预期标头。 例如’application / vnd.mycompany.mytype + v2’和’application / vnd.mycompany.mytype + v1’。 不需要进行任何其他更改,您可以继续使用Jersey
  • 切换框架。 RESTEasy恰好可以轻松处理您的场景。

使用RESTEasy和资源:

 @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(); } } 

使用以下Accept标头成功匹配:

接受:application / vnd.test; version = 1; q = 0.3,application / vnd.test; version = 2; q = 0.5
回复:版本2

和这个:

接受:application / vnd.test; version = 1; q = 0.5,application / vnd.test; version = 2; q = 0.3
回复:版本1

您可以使用此示例项目下载和测试。 需要Git,Maven和JBoss 7.x.

除非我遗漏了什么。 JAX-RS确实支持Accept参数。 看看@Consumes("*/*")注释。 此外,由于您在同一个url上有两个GET方法,因此会出现媒体类型冲突的exception。 使用@Path("test2")注释test2()方法,然后将您的GET请求发送到url / test2。 应该摆脱那个错误。

编辑

您可以使用@HeaderParams注入Accept标头的@HeaderParams 。 这是我所做的一个例子。

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

传递请求

接受:application / vnd.test; version = 2,application / vnd.test; version = 1; q = 0.1

这将打印

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

然后,您可以手动处理它。 这是你在找什么?

使用Jersey框架,HTTP请求的Accept标头声明了最可接受的内容。 如果资源类能够生成多于一种MIME媒体类型,则所选择的资源方法将对应于客户端声明的最可接受的媒体类型。 在您的情况下,如果接受标头是

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

然后将调用方法test1()。

如果是

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

后者将被称为。

可以在同一个@Produces声明中声明多个媒体类型,例如:

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

测试(如果2种介质类型中的任何一种都是可接受的,则将调用9方法。如果两者都是可接受的,则将调用第一种。

希望能帮助到你!