14

我试图弄清楚@Consumes 是如何在这里工作的。

我有一个如下所示的简化资源,我只希望该资源使用“application/vnd.myApp+xml”。

@Path("/app")
@Consumes("application/vnd.myApp+xml")
@Produces("application/vnd.myApp+xml")
public class AppResource {
    @POST
    public Response postStuff() {
        ...
    }
}

我有以下测试用例:-

public class AppResourceTest extends JerseyTest {
    @Test
    public void testApp() {
        // #1: Works fine
        ClientResponse response = resource().path("app")
                    .accept("application/vnd.myApp+xml")
                    .post(ClientResponse.class);

        ...

        // #2: Throws a 415 Unsupported Media Type
        ClientResponse response = resource().path("app")
                    .accept("application/vnd.myApp+xml")
                    .type("text/plain")
                    .post(ClientResponse.class);

        ...

        // #3: Works fine
        ClientResponse response = resource().path("app")
                    .accept("application/vnd.myApp+xml")
                    .type("application/vnd.myApp+xml")
                    .post(ClientResponse.class);

        ...
    }
}

从上面的 3 个测试中,#2 和 #3 按预期工作。

至于#1,如果我不设置内容类型,为什么它也不会抛出415?

4

3 回答 3

9

基于@Consumes api ( http://jsr311.java.net/nonav/releases/1.0/javax/ws/rs/Consumes.html ) 和 HTTP 类型规范 ( http://www.w3.org/Protocols/ rfc2616/rfc2616-sec7.html#sec7.2.1)加上您看到的行为,我认为可以安全地得出以下 Jersey 实现:

如果客户端未设置 Content-Type,则 Jersey 不会默认设置,但允许它通过任何/所有 @Consumes 注释。

当为 URI 设置了多个 @Consumes {不同类型}并且客户端没有设置 Content-Type 时,Jersey 将默认为第一个 @Consumes 注释或可接受类型列表中的第一个类型。

当设置 Accepts 标头值时,Jersey 将找到执行的最佳拟合方法。如果多个方法最适合,它将默认为第一个定义的。

总之,@Consumes 仅当且仅当客户端设置 Content-Type 时才充当过滤器,否则 Jersey 将尝试找到最合适的匹配。这确实符合 HTTP 规范:

任何包含实体主体的 HTTP/1.1 消息都应该包含定义该主体的媒体类型的 Content-Type 头字段。当且仅当媒体类型不是由 Content-Type 字段给出时,接收者可以尝试通过检查其内容和/或用于识别资源的 URI 的名称扩展来猜测媒体类型。如果媒体类型仍然未知,接收者应该将其视为类型“application/octet-stream”。

如果目标是让@Consumes 充当白名单,那么可以使用 servlet 过滤器在未设置请求的情况下默认 Content-Type。

于 2013-02-14T16:09:52.320 回答
2

根据似乎@Consumes在类级别使用的文档并没有显式覆盖方法级别定义(默认为*/*),因此它可能以附加方式工作是有道理的......

您是否尝试过@Consumes在方法定义上应用相同的方法?

于 2012-11-10T05:47:35.993 回答
2

您应该指定类型 - 例如:

ClientResponse res =
    service.path("accounts")
        .type("application/vnd.dsu.account+json")
        .post(ClientResponse.class,ent);
于 2012-11-13T09:47:38.513 回答