我正在尝试编写一个简单的Jersey应用程序,它将文件从Jersey客户端发送到Jersey服务器并返回。但是,文件似乎只是在从客户端到服务器的路上编码而不是其他方式。我想知道如何改变这种行为。
我在一个简单的例子中对此进行测试:
public class GZipEncodingTest extends JerseyTest {
private static final String PATH = "/";
private static final String QUESTION = "foo", ANSWER = "bar";
private static final String ENCODING_GZIP = "gzip";
@Path(PATH)
public static class MyResource {
@POST
public Response handle(String question) throws IOException {
assertEquals(QUESTION, question);
return Response.ok(ANSWER).build(); // (1)
}
}
@Override
protected Application configure() {
enable(TestProperties.LOG_TRAFFIC);
enable(TestProperties.DUMP_ENTITY);
return new ResourceConfig(MyResource.class, GZipEncoder.class);
}
@Override
@SuppressWarnings("unchecked")
protected void configureClient(ClientConfig config) {
config.register(new EncodingFeature(ENCODING_GZIP, GZipEncoder.class));
}
@Test
public void testHeaders() throws Exception {
Response response = target().path(PATH).request().post(Entity.text(QUESTION));
assertEquals(ANSWER, response.readEntity(String.class));
}
}
从记录的转储中,我可以看出请求是按预期的:内容编码在标头中发出信号并应用在请求消息正文中。 Accept-Encoding 也已设置。服务器了解应用的gzip压缩并解压缩请求消息正文。但是,它忽略了客户端接受gzip压缩响应并发送未压缩响应消息体的事实。
当我在encoding(ENCODING_GZIP)
- 构建链中的行(1)中追加Response
时,我会得到我正在寻找的结果。但是,我想仅在请求中将其标记为可接受时才应用编码。此外,我希望将此功能应用于整个范围,而不仅仅是针对特定的响应。
我当然可以使用WriterInterceptor
手动添加此功能:
public class GZipWriterInterceptor implements WriterInterceptor {
@Override
public void aroundWriteTo(WriterInterceptorContext context)
throws IOException, WebApplicationException {
context.getHeaders().add(HttpHeaders.CONTENT_ENCODING, ENCODING_GZIP);
context.proceed();
}
}
但我确信这是不必要的锅炉。
EncodingFeature
似乎只是客户端库的一部分。我基本上正在寻找一种可能性,当请求通过accept-encoding建议编码时,Jersey服务器将数据编码为gzip。
当我尝试在网上搜索解决方案时,我发现很多。他们中的大多数都关注Jersey 1.他们中的一些人建议为GrizzlyServer添加一个监听器(这将是Jersey特定而不是JAX-RS?)。然后在Jersey 2依赖树中有很多类建议使用GZip编码:
org.glassfish.grizzly.http.GZipContentEncoding
org.glassfish.jersey.message.GZipEncoder
org.glassfish.grizzly.compression.zip.GZipEncoder
org.glassfish.grizzly.compression.zip.GZipDecoder
org.glassfish.grizzly.compression.zip.GZipFilter
我发现网络上的人建议使用其中任何一个,即使我认为org.glassfish.jersey
似乎是正确的选择,因为它是一个真正的泽西依赖。更不用说ApacheConnector
相关库中的那些。我不知道我应该使用哪一个。
答案 0 :(得分:19)
我通过浏览泽西岛图书馆想出来了。对于服务器端,需要以下配置:
@Override
@SuppressWarnings("unchecked")
protected Application configure() {
ResourceConfig resourceConfig = new ResourceConfig(MyResource.class);
EncodingFilter.enableFor(resourceConfig, GZipEncoder.class);
return resourceConfig;
}
在转化后,EncodingFilter#enableFor(ResourceConfig.Class<? extends ContentEncoder>[])
向EncodingFilter
注册GZipEncoder
,并在指定的ResourceConfig
注册EncodingFilter
。
我想在注册中绕道而行的原因在于任何编码都需要分两个阶段进行。首先,ContainerResponseFilter
(实际Content-Encoding
通过将gzip
设置为WriterInterceptor
来修改响应的标头。同时,过滤器无法修改实体流由于在甚至创建此流之前调用了过滤器,因此必须通过在处理过滤器之后以及在创建实体流之后触发的GZipEncoder
来处理流修改。
出于这个原因,只有当Content-Encoding
标头由客户端设置为gzip
时,只有注册EncodingFilter
才能用于请求解码,这与服务器的创建无关。
我使用'GZipWriterInterceptor'给出的示例基本上是GZipEncoder
的实现版本。当然,标题应该设置在过滤器中,而不是设置在拦截器中。它says in the documentation:
而过滤器主要用于操纵请求和 响应参数,如HTTP头,URI和/或HTTP方法, 拦截器旨在通过操纵来操纵实体 实体输入/输出流
因此,gzip编码不能简单地通过注册Feature
来激活,它也需要在过滤器中注册。这就是为什么我希望两者都捆绑在一个EncodingFilter
。
重要:泽西岛内有两个{{1}}个班级。一个属于客户端,另一个属于服务器实现。不要使用错误的,因为他们做了根本不同的事情。不幸的是,在运行单元测试时,您将在类路径上拥有它们,因为它们依赖于客户端接口。