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

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

问题描述

我正在尝试编写一个简单的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,题);
return Response.ok(ANSWER).build(); //(1)
}
}

@Override
protected Application(){
enable(TestProperties.LOG_TRAFFIC);
启用(TestProperties.DUMP_ENTITY);
返回新的ResourceConfig(MyResource.class,GZipEncoder.class);

$ b @覆盖
@SuppressWarnings(unchecked)
protected void configureClient(ClientConfig config){
config.register(new EncodingFeature(ENCODING_GZIP ,GZipEncoder.class));

$ b @Test
public void testHeaders()throws Exception {
Response response = target()。path(PATH).request()。post(Entity的.text(QUESTION));
assertEquals(ANSWER,response.readEntity(String.class));




$ b $ p
$ b从记录的转储中,我可以知道请求如预期的那样:内容编码在报头中被发信号并被应用在请求消息主体上。 Accept-Encoding 也被设置。服务器了解应用的gzip压缩并解压缩请求消息体。但是,它忽略了这样一个事实,即客户端接受一个gzip压缩的响应并发送解压缩的响应消息体。



当我添加 encoding(ENCODING_GZIP)响应 -builder链中行(1),我得到了我正在寻找的结果。但是,我只想应用编码,如果它在请求中被标记为可接受的。此外,我想结合这个功能的应用范围广泛,不仅为特定的响应。



我当然可以添加一个这样的功能与一个 WriterInterceptor

  public class GZipWriterInterceptor implements WriterInterceptor {
@Override
public void aroundWriteTo(WriterInterceptorContext上下文)
抛出IOException,WebApplicationException {
context.getHeaders()。add(HttpHeaders.CONTENT_ENCODING,ENCODING_GZIP);
context.proceed();
}
}

但我确信这是不必要的锅炉板。

EncodingFeature 似乎只是客户端库的一部分。我基本上在寻找一种可能性,让泽西服务器将数据编码为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。球衣似乎是正确的选择,因为它实际上是泽西岛的依赖。不要说在 ApacheConnector 相关库中找到的那些。我不知道应该使用哪一个。

解决方案

我通过查看泽西图书馆了解了这一点。对于服务器端来说,需要以下配置:

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

$ / code>

在转换下, EncodingFilter#enableFor(ResourceConfig。 Class< ;? extends ContentEncoder> [])注册一个 EncodingFilter 和指定的 GZipEncoder 给定的 ResourceConfig



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



因此,当内容编码时,只有注册 GZipEncoder 才能用于请求解码 header被客户端设置为 gzip ,它独立于服务器创建。



我用'GZipWriterInterceptor'给出的例子基本上是 EncodingFilter 的一个糟糕的版本。当然,标题应该设置在过滤器中,而不是一个拦截器。它在文档中说过:


尽管过滤器主要用于处理HTTP头,URI和/或HTTP方法之类的请求和
响应参数,但
拦截器旨在通过操作
实体输入/输出流

因此,gzip编码不能简单地通过注册 GZipEncoder ,它也需要注册一个过滤器。这就是为什么我希望将它们捆绑在功能中。



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


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));
  }
}

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.

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.

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.

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.

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

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.

解决方案

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;
}

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

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.

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.

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:

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

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.

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天全站免登陆