简体   繁体   English

如何处理 JAVA servlet 中的压缩 (gzip) HTTP 请求(不响应) - 简单示例?

[英]How to handle compressed (gzip) HTTP requests (NOT response) in JAVA servlet - Simple Example?

I struggled with this problem for quite some time;我在这个问题上挣扎了很长一段时间; and after finding a simple solution... wanted to ask a question & answer!!在找到一个简单的解决方案之后......想问一个问题和答案!

The question has been asked in different ways multiple times on stack overflow, and the accepted solutions are either partially correct and complex or talk about response compression.这个问题在堆栈溢出时以不同的方式被多次提出,并且accepted solutions要么是partially correct and complex的,要么是关于response压缩的。

Aggregating some old Q&A on this topic:汇总有关此主题的一些旧问答:

A simple solution is by using a filter.一个简单的解决方案是使用过滤器。 ( See servlet-filter tutorial ) 参见 servlet-filter 教程

Create a Servlet Filter:创建一个 Servlet 过滤器:

  • Make sure that the filter is called either first/before any filters which use request body.确保首先/在任何使用请求正文的过滤器之前调用过滤器。

I. Register filter in web.xml:一、web.xml中的寄存器过滤器:

<filter>
    <filter-name>GzipRequestFilter</filter-name>
    <filter-class>com...pkg...GzipRequestFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>GzipRequestFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

II.二、 Code for filter class:滤波器代码 class:

public class GzipRequestFilter implements Filter {
    // Optional but recommended.
    private static final Set<String> METHODS_TO_IGNORE = ImmutableSet.of("GET", "OPTIONS", "HEAD");

    @Override
    public void doFilter(
            final ServletRequest request,
            final ServletResponse response,
            final FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        
        String method = httpServletRequest.getMethod().toUpperCase();
        String encoding = Strings.nullToEmpty(
            httpServletRequest.getHeader(HttpHeaders.CONTENT_ENCODING));

        if (METHODS_TO_IGNORE.contains(method) || !encoding.contains("application/gzip")) {
            chain.doFilter(request, response); // pass through
            return;
        }
        
        HttpServletRequestWrapper requestInflated = new GzippedInputStreamWrapper(httpServletRequest);
        chain.doFilter(requestInflated, response);
    }

    @Override
    public void init(final FilterConfig filterConfig) throws ServletException {}
    @Override
    public void destroy() {}
}

III.三、 Followed by code for GzipInputStream wrapper:接下来是 GzipInputStream 包装器的代码:

// Simple Wrapper class to inflate body of a gzipped HttpServletRequest.
final class GzippedInputStreamWrapper extends HttpServletRequestWrapper {
    private GZIPInputStream inputStream;

    GzippedInputStreamWrapper(final HttpServletRequest request) throws IOException {
        super(request);
        inputStream = new GZIPInputStream(request.getInputStream());
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        return new ServletInputStream() {
            // NOTE: Later versions of javax.servlet library may require more overrides.
            public int read() throws IOException {
                return inputStream.read();
            }
            public void close() throws IOException {
                super.close();
                inputStream.close();
            }
        };
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(inputStream));
    }
}

Now what remains is how to send a compressed request?现在剩下的是如何发送压缩请求?

Postman does not yet support sending compressed HttpRequest bodies. Postman 尚不支持发送压缩的HttpRequest正文。 You can still make it work by using the binary option and use a gzipped file containing the properly encoded request body.您仍然可以通过使用binary选项并使用包含正确编码的请求正文的 gzip 压缩文件来使其工作。

One way is using a nodejs script with pako compression library .一种方法是使用带有pako压缩库的 nodejs 脚本。 For a multipart/form-data request see form-data library对于 multipart/form-data 请求,请参见form-data library

const pako = require('pako')
const axios = require('axios')

var params = qs.stringify({
  'num': 42,
  'str': 'A string param',
});

data = pako.gzip(Buffer.from(params));

var config = {
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded',
    'Content-Encoding': 'application/gzip';
  },
}

await axios.post(
  'http://url-for-post-api-accepting-urlencoded',
  data,
  config,
).then((res) => {
  console.log(`status: ${res.status} | data: ${res.data}`)
}).catch((error) => {
  console.error(error)
})

NOTES:笔记:

  • We are using Content-Encoding: application/gzip header to specify that a request is compressed.我们使用Content-Encoding: application/gzip header 来指定请求被压缩。 Yes this is standard.是的,这是标准的。
  • Do not use Content-Type as it will not work with multipart/form-data .不要使用Content-Type ,因为它不适用于multipart/form-data
  • The HTTP protocol has been running under the assumption that size of HttpRequests are dwarfed by HttpResponses. HTTP 协议一直在假设 HttpRequest 的大小与 HttpResponse 相比相形见绌。
  • Further, due to assumed limited computing power in browser/client side, the norm has been to compress response and not requests.此外,由于假定浏览器/客户端的计算能力有限,规范一直是压缩响应而不是请求。 Browsers cannot natively compress but can do decompression natively.浏览器不能原生压缩,但可以原生解压。
  • But, unfortunately after years of many developers pushing code;但是,不幸的是,在许多开发人员推动代码多年之后; some HTTP APIs evolve to consume large strings/data!!一些 HTTP API 演变为消耗大字符串/数据!
  • It's a piece of cake to allow java servlets to have the option of working with compressed requests.允许 java servlets 选择处理压缩请求是小菜一碟。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM