2013-11-04 96 views
12

我正在尝试编写一个简单的Jersey应用程序,它将Jersey客户机中的文件发送到Jersey服务器并返回。但是,这些文件似乎只是在从客户端到服务器的路上编码,而不是以其他方式编码。我想知道如何改变这种行为。如何使Jersey对响应消息体使用GZip压缩

我在一个简单的例子测试该:

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 -builder链,我得到我期待的结果。但是,我只想应用编码,如果它在请求中被标记为可接受的。此外,我希望广泛地结合此功能应用程序,而不仅仅是针对特定的响应。

我当然可以用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似乎只是客户端库的一部分。我基本上希望有一种可能性,当请求通过接受编码建议编码时,Jersey服务器将数据编码为gzip。

当我尝试在网上搜索解决方案时,发现很多。他们中的大多数都关注泽西岛1.他们中的一些人建议增加一个监听器到GrizzlyServer(这将是泽西特定的而不是JAX-RS?)。再就是泽西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

查看http://stackoverflow.com/questions/19751014/gzip-encoding-in-jersey-2-grizzly – 2013-11-04 20:39:39

+0

嗯,我的问题其实有点复杂。我想重建gzip编码。我在这里重申我的问题:http://stackoverflow.com/questions/19794014/why-does-jersey-swallow-my-content-encoding-header –

+0

我仍然试图重建*官方解决方案*通过获取此问题GZip示例工作。但是,我只有一种方式,而不是另一种。说实话,你建议的解决方案对我来说看起来像是一种黑客攻击。 –

回答

16

我通过浏览泽西岛图书馆了解到。对于服务器侧,下面的配置是必要的:

@Override 
@SuppressWarnings("unchecked") 
protected Application configure() { 
    ResourceConfig resourceConfig = new ResourceConfig(MyResource.class); 
    EncodingFilter.enableFor(resourceConfig, GZipEncoder.class); 
    return resourceConfig; 
} 

根据convers,EncodingFilter#enableFor(ResourceConfig.Class<? extends ContentEncoder>[])注册一个EncodingFilter和指定GZipEncoder与给定ResourceConfig

我猜这个注册背后的原因在于,任何编码都需要在两个阶段发生。首先,EncodingFilter(其实是ContainerResponseFilter通过将Content-Encoding设置为gzip来修改响应的头部)。同时,由于在创建该流之前调用过滤器,过滤器不能修改消息体的实体流。因此,流修改必须由该过滤器的处理之后,并且还创建实体流的后触发的WriterInterceptor进行处理。

出于这个原因,只注册GZipEncoder将针对该请求的解码工作中当Content-Encoding头部设置为gzip由客户端独立于服务器的创建而生成

我用'GZipWriterInterceptor'给出的例子基本上是EncodingFilter的一个执行不当的版本。当然,标题应该设置在过滤器中,而不是拦截器中。它says in the documentation

鉴于过滤器主要是用来操纵请求并 响应参数,如HTTP标头,URI和/或HTTP方法, 拦截器意图操纵的实体,通过操纵 实体的输入/输出流

因此,gzip编码不能简单地通过注册GZipEncoder来激活,它也需要注册一个过滤器。这就是为什么我预计这两个版本都会被捆绑在Feature之中。

重要:有内泽西2 EncodingFilter类。一个属于客户端,另一个属于服务器实现。不要使用错误的东西,因为它们做了根本不同的事情。不幸的是,由于它们依赖于客户端接口,所以在运行单元测试时,你将在你的类路径中拥有它们。

+1

这是非常可怕的...这个解决方案仅适用于泽西岛2.0+ – Filipe

+0

1x(1.6)的任何指针? –