为了处理内容类型的不同版本,我尝试使用“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();
}
媒体类型冲突异常的结果:
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对于accept-parameters没有明确规定。
到目前为止,我正在使用内容类型,其中包含其名称中的版本,但我发现这个解决方案非常流畅。
@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)。但我不知道是哪一个。
答案 0 :(得分:16)
JAX-RS规范显式声明忽略Accept头参数的任何内容。但处理明确定义的唯一参数是 quality (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'执行'唯一性'检查,完全省略任何参数。这可以通过在“匹配”资源方法上使用各种标题对进行测试来确认:
Resource 1 Resource 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, quality 参数仅在处理可能的内容类型的 mix 时才有意义。但是,当发送非质量参数或其他参数时,这种有限匹配会失败:
接受:text / html;版本= 4.0; q = 0.8,text / html;版本= 3.2; q = 0.4
那么可能的解决方案是什么?
使用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
您可以使用此sample project下载和测试。需要Git,Maven和JBoss 7.x
答案 1 :(得分:2)
除非我遗漏了什么。 JAX-RS确实支持Accept参数。查看@Consumes("*/*")
注释。此外,由于您在同一个网址上有两个GET
方法,因此会出现媒体类型冲突的异常。使用@Path("test2")
注释test2()方法,然后将您的GET
请求发送到url / test2。应该摆脱那个错误。
修改强>
您可以使用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
然后您可以手动处理它。这是你在找什么?
答案 2 :(得分: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方法。如果两者都可接受,则将调用第一种。
希望它有所帮助!