如何让 Jersey 对响应消息正文使用 GZip 压缩 [英] How to make Jersey use GZip compression for the response message body

查看:38
本文介绍了如何让 Jersey 对响应消息正文使用 GZip 压缩的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试编写一个简单的 Jersey 应用程序,将文件从 Jersey 客户端发送到 Jersey 服务器并返回.但是,这些文件似乎只在从客户端到服务器的过程中被编码,而不是在其他方式.我想知道如何改变这种行为.

I am trying to write a simple Jersey application that sends files from a Jersey client to a Jersey server and back. However, the files only seem to be encoded on the way from the client to the server but not the other way. I wonder how I can change this behavior.

我在一个简单的例子中对此进行测试:

I am testing this in a simple example:

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 压缩并解压缩请求消息正文.但是,它忽略了客户端接受压缩后的响应并发送未压缩的响应消息体这一事实.

From the logged dump, I can tell that the request is as intended: the content encoding is signaled in the header and applied on the request message body. The Accept-Encoding is also set. The server understands the applied gzip compression and unzips the request message body. However, it ignores the fact that the client accepts a gzipped response and sends the response message body uncompressed.

当我在 Response-builder 链的 (1) 行中附加 encoding(ENCODING_GZIP) 时,我得到了我正在寻找的结果为了.但是,我只想在请求中将其标记为可接受的情况下应用编码.此外,我想在整个应用程序范围内联合此功能,而不仅仅是针对特定响应.

When I append encoding(ENCODING_GZIP) in line (1) in the Response-builder chain, I get the result I am looking for. However, I want to only apply the encoding if it was marked as acceptable in the request. Furthermore, I want to ally this feature application wide and not only for specific responses.

我当然可以使用 WriterInterceptor 手动添加这样的功能:

I can of course add such a feature manually with a 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();
  }
}

但我确信这是不必要的样板.

but I am convinced that this is unnecessary boiler plate.

EncodingFeature 似乎只是客户端库的一部分.每当请求通过接受编码建议编码时,我基本上都在寻找一种可能性,让泽西服务器将数据编码为 gzip.

The EncodingFeature seems to only be a part of the client library. I am basically looking for a possibility to make the Jersey server encode data as gzip whenever the request suggested the encoding via accept-encoding.

当我尝试在网络上搜索解决方案时,我发现了很多.他们中的大多数都关注 Jersey 1.其中一些建议向 GrizzlyServer 添加一个侦听器(这将是 Jersey 特定的而不是 JAX-RS?).然后在 Jersey 2 依赖树中有很多类建议使用 GZip 编码:

When I try to search for solutions on the web, I find plenty. Most of them concern Jersey 1. Some of them suggest adding a listener to the GrizzlyServer (which would be Jersey specific and not JAX-RS?). Then there are plenty classes within the Jersey 2 dependency tree that suggest GZip encoding:

  • 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 似乎是正确的选择,因为它是一个实际的 Jersey 依赖项.更不用说在 ApacheConnector 相关库中找到的那些了.我不知道我应该实际使用哪一个.

I found that people on the web suggest using any of them even though I like to think that org.glassfish.jersey seems to be the right choice since it is an actual Jersey dependency. Not to speak of those that are found in the ApacheConnector related libraries. I have no idea which one I should actually use.

推荐答案

我通过查看 Jersey 库找到了答案.对于服务器端,需要进行如下配置:

I figured it out by looking through the Jersey library. For the server side, the following configuration is necessary:

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

在convers下,EncodingFilter#enableFor(ResourceConfig.Class[])注册一个EncodingFilter和指定的GZipEncoder使用给定的 ResourceConfig.

Under the convers, EncodingFilter#enableFor(ResourceConfig.Class<? extends ContentEncoder>[])registers an EncodingFilter and the specified GZipEncoder with the given ResourceConfig.

我猜想注册过程中出现这种绕道的原因在于任何编码都需要分两个阶段进行.首先,EncodingFilter(这是一个实际的 ContainerResponseFilter 通过将 Content-Encoding 设置为 gzip. 同时,过滤器不能修改消息体的实体流,因为过滤器在这个流创建之前就被调用了,所以流的修改必须由一个WriterInterceptor来处理,它是在过滤器处理之后以及实体流创建之后触发.

I guess the reason behind this detour in the registration lies in the fact that any encoding needs to happen in two stages. First, the EncodingFilter (which is an actual ContainerResponseFilter modifies the header of the response by setting Content-Encoding to gzip. At the same time, a filter cannot modify the entity stream of the message body since the filter is invoked before this stream is even created. Therefore, the stream modification must be processed by a WriterInterceptor which is triggered after the processing of the filter and also after the creation of the entity stream.

因此,当 Content-Encoding 标头由独立于服务器创建的客户端.

For this reason, only registering the GZipEncoder will work for the request decoding when the Content-Encoding header is set to gzip by the client which occurs independently of the server's creation.

我用GZipWriterInterceptor"给出的示例基本上是 EncodingFilter 的一个实施不佳的版本.当然,标头应该设置在过滤器中而不是拦截器中.它在文档中说:

The example I gave with my 'GZipWriterInterceptor' is basically a poorly implemented version of the EncodingFilter. Of course, the header should be set in a filter and not in an interceptor. It says in the documentation:

而过滤器主要用于操纵请求和响应参数,例如 HTTP 标头、URI 和/或 HTTP 方法,拦截器旨在通过操纵来操纵实体实体输入/输出流

Whereas filters are primarily intended to manipulate request and response parameters like HTTP headers, URIs and/or HTTP methods, interceptors are intended to manipulate entities, via manipulating entity input/output streams

因此,gzip 编码不能简单地通过注册一个 GZipEncoder 来激活,它也需要注册一个过滤器.这就是为什么我希望两者都捆绑在一个 Feature 中.

Therefore, gzip encoding cannot simply be activated by registering a GZipEncoder, it needs to be registered with a filter, as well. This is why I expected both to be bundled in a Feature.

重要提示:Jersey 中有两个 EncodingFilter 类.一个属于客户端,另一个属于服务器实现.不要使用错误的,因为它们做的事情根本不同.不幸的是,由于它们依赖于客户端接口,因此在运行单元测试时,它们都会出现在类路径中.

Important: There are two EncodingFilter classes within Jersey. One belongs to the client, the other belongs to the server implementation. Do not use the wrong one since they do fundamentally different things. Unfortunately, you will have both of them on your class path when running unit tests since they rely on the client interface.

这篇关于如何让 Jersey 对响应消息正文使用 GZip 压缩的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆