泽西岛:@Consumes在未设置内容类型时不起作用

2022-09-04 03:26:13

我试图弄清楚@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?


答案 1

基于@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)以及您看到的行为,我认为可以安全地得出以下泽西岛实现的结论:

如果内容类型不是由客户端设置的,则泽西岛不是默认的,但允许它通过任何/所有@Consumes注释。

如果为 URI 设置了多个@Consumes {不同的类型},并且客户端尚未设置内容类型,则 Jersey 将默认为可接受类型列表中的第一个@Consumes注释或第一个类型。

设置“接受”标头值后,泽西岛将找到最适合执行的方法。如果多个方法最适合,它将默认为第一个定义的方法。

总之,只有当且仅当客户端设置了内容类型时,@Consumes才充当过滤器,否则泽西岛将尝试找到最合适的匹配项。这确实符合 HTTP 规范:

任何包含实体正文的 HTTP/1.1 消息都应包含定义该正文的媒体类型的内容类型标头字段。当且仅当媒体类型不是由“内容类型”字段给出的,收件人可能会尝试通过检查媒体类型的内容和/或用于标识资源的 URI 的扩展名来猜测媒体类型。如果媒体类型仍然未知,收件人应将其视为类型“应用程序/八位字节流”。

如果目标是让@Consumes充当白名单,则可以使用 servlet 过滤器在未设置任何内容的请求上默认 Content-Type。


答案 2

您应该指定类型 - 例如:

ClientResponse res =
    service.path("accounts")
        .type("application/vnd.dsu.account+json")
        .post(ClientResponse.class,ent);