在开发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方法。如果两者都是可接受的,则将调用第一种。
希望能帮助到你!