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

Arn*_*rno 10 java rest jax-rs

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

Accept: application/vnd.mycompany.mytype;version=2 , application/vnd.mycompany.mytype;version=1;q=0.1
Run Code Online (Sandbox Code Playgroud)

问题是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();
}
Run Code Online (Sandbox Code Playgroud)

导致媒体类型冲突异常:

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
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

编辑

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

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

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

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

Per*_*ion 16

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();
}
Run Code Online (Sandbox Code Playgroud)

看来泽西岛根据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,质量参数只有在处理各种可能的内容类型时才有意义.但是,当发送非质量参数或其他参数时,这种有限匹配会失败:

接受: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();
    }
}
Run Code Online (Sandbox Code Playgroud)

使用以下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.