简体   繁体   English

如何在 Spring MVC 中解码 Gzip 压缩的请求正文

[英]How to decode Gzip compressed request body in Spring MVC

I have a client that sends data with我有一个客户端发送数据

CONTENT-ENCODING deflate

I have code like this我有这样的代码

@RequestMapping(value = "/connect", method = RequestMethod.POST)
@ResponseBody
public Map onConnect(@RequestBody String body){}

Currently 'body' prints out the garbled, compressed data.当前,'body' 打印出乱码的压缩数据。 Is there any way to make Spring MVC automatically uncompress it?有什么办法可以让Spring MVC自动解压吗?

You'll need to write you own filter to unzip body of gzipped requests.您需要编写自己的过滤器来解压缩 gzip 请求的主体。 Sine you will be reading the whole input stream from request you need to override parameters parsing method too.由于您将从请求中读取整个输入流,因此您也需要覆盖参数解析方法。 This is the filter I'm using in my code.这是我在代码中使用的过滤器。 Supports only gzipped POST requests, but you can update it to use other types of requests if needed.仅支持 gzip 压缩的 POST 请求,但您可以根据需要更新它以使用其他类型的请求。 Also beware to parse parameters I'm using guava library, you can grab yours from here: http://central.maven.org/maven2/com/google/guava/guava/还要注意解析我正在使用番石榴库的参数,您可以从这里获取您的参数: http : //central.maven.org/maven2/com/google/guava/guava/

public class GzipBodyDecompressFilter extends Filter {
    
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        
    }
    /**
     * Analyzes servlet request for possible gzipped body.
     * When Content-Encoding header has "gzip" value and request method is POST we read all the
     * gzipped stream and is it haz any data unzip it. In case when gzip Content-Encoding header
     * specified but body is not actually in gzip format we will throw ZipException.
     *
     * @param servletRequest  servlet request
     * @param servletResponse servlet response
     * @param chain           filter chain
     * @throws IOException      throws when fails
     * @throws ServletException thrown when fails
     */
    @Override
    public final void doFilter(final ServletRequest servletRequest,
                               final ServletResponse servletResponse,
                               final FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        boolean isGzipped = request.getHeader(HttpHeaders.CONTENT_ENCODING) != null
                && request.getHeader(HttpHeaders.CONTENT_ENCODING).contains("gzip");
        boolean requestTypeSupported = HttpMethods.POST.equals(request.getMethod());
        if (isGzipped && !requestTypeSupported) {
            throw new IllegalStateException(request.getMethod()
                    + " is not supports gzipped body of parameters."
                    + " Only POST requests are currently supported.");
        }
        if (isGzipped && requestTypeSupported) {
            request = new GzippedInputStreamWrapper((HttpServletRequest) servletRequest);
        }
        chain.doFilter(request, response);

    }

    /**
     * @inheritDoc
     */
    @Override
    public final void destroy() {
    }

    /**
     * Wrapper class that detects if the request is gzipped and ungzipps it.
     */
    final class GzippedInputStreamWrapper extends HttpServletRequestWrapper {
        /**
         * Default encoding that is used when post parameters are parsed.
         */
        public static final String DEFAULT_ENCODING = "ISO-8859-1";

        /**
         * Serialized bytes array that is a result of unzipping gzipped body.
         */
        private byte[] bytes;

        /**
         * Constructs a request object wrapping the given request.
         * In case if Content-Encoding contains "gzip" we wrap the input stream into byte array
         * to original input stream has nothing in it but hew wrapped input stream always returns
         * reproducible ungzipped input stream.
         *
         * @param request request which input stream will be wrapped.
         * @throws java.io.IOException when input stream reqtieval failed.
         */
        public GzippedInputStreamWrapper(final HttpServletRequest request) throws IOException {
            super(request);
            try {
                final InputStream in = new GZIPInputStream(request.getInputStream());
                bytes = ByteStreams.toByteArray(in);
            } catch (EOFException e) {
                bytes = new byte[0];
            }
        }


        /**
         * @return reproduceable input stream that is either equal to initial servlet input
         * stream(if it was not zipped) or returns unzipped input stream.
         * @throws IOException if fails.
         */
        @Override
        public ServletInputStream getInputStream() throws IOException {
            final ByteArrayInputStream sourceStream = new ByteArrayInputStream(bytes);
            return new ServletInputStream() {
                public int read() throws IOException {
                    return sourceStream.read();
                }

                public void close() throws IOException {
                    super.close();
                    sourceStream.close();
                }
            };
        }

        /**
         * Need to override getParametersMap because we initially read the whole input stream and
         * servlet container won't have access to the input stream data.
         *
         * @return parsed parameters list. Parameters get parsed only when Content-Type
         * "application/x-www-form-urlencoded" is set.
         */
        @Override
        public Map getParameterMap() {
            String contentEncodingHeader = getHeader(HttpHeaders.CONTENT_TYPE);
            if (!Strings.isNullOrEmpty(contentEncodingHeader)
                    && contentEncodingHeader.contains("application/x-www-form-urlencoded")) {
                Map params = new HashMap(super.getParameterMap());
                try {
                    params.putAll(parseParams(new String(bytes)));
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                return params;
            } else {
                return super.getParameterMap();
            }
        }

        /**
         * parses params from the byte input stream.
         *
         * @param body request body serialized to string.
         * @return parsed parameters map.
         * @throws UnsupportedEncodingException if encoding provided is not supported.
         */
        private Map<String, String[]> parseParams(final String body)
                throws UnsupportedEncodingException {
            String characterEncoding = getCharacterEncoding();
            if (null == characterEncoding) {
                characterEncoding = DEFAULT_ENCODING;
            }
            final Multimap<String, String> parameters = ArrayListMultimap.create();
            for (String pair : body.split("&")) {
                if (Strings.isNullOrEmpty(pair)) {
                    continue;
                }
                int idx = pair.indexOf("=");

                String key = null;
                if (idx > 0) {
                    key = URLDecoder.decode(pair.substring(0, idx), characterEncoding);
                } else {
                    key = pair;
                }
                String value = null;
                if (idx > 0 && pair.length() > idx + 1) {
                    value = URLDecoder.decode(pair.substring(idx + 1), characterEncoding);
                } else {
                    value = null;
                }
                parameters.put(key, value);
            }
            return Maps.transformValues(parameters.asMap(),
                    new Function<Collection<String>, String[]>() {
                        @Nullable
                        @Override
                        public String[] apply(final Collection<String> input) {
                            return Iterables.toArray(input, String.class);
                        }
                    });
        }
    }
}

This should be handled by the server, not the application.这应该由服务器处理,而不是应用程序。

As far as I know, Tomcat doesn't support it, though you could probably write a filter.据我所知,Tomcat 不支持它,尽管您可以编写一个过滤器。

A common way to handle this is to put Tomcat ( or whatever Java container you're using) behind an Apache server that is configured to handle compressed request bodies.处理此问题的常用方法是将 Tomcat(或您使用的任何 Java 容器)置于配置为处理压缩请求正文的 Apache 服务器之后。

You don't handle it in Spring.你不会在 Spring 中处理它。 Instead you use a filter so that the data arrives in Spring already deflated.相反,您使用过滤器,以便数据到达 Spring 时已经放气。

Hopefully these two links can get you started.希望这两个链接可以帮助您入门。

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

相关问题 Spring MVC:如何在请求浏览器中设置主体参数,以及如何在Spring MVC的控制器中获取该主体参数? - Spring MVC: How to set body parameter in request browser and how to get this body parameters in controller in Spring MVC? Spring MVC:请求正文中的JSONObject请求映射 - Spring MVC: Request Mapping for JSONObject in Request body 如何在请求正文 spring mvc 中发送带有实体对象的额外字段? - How to send extra fields with an entity object in request body spring mvc? Spring MVC Controller预处理请求正文 - Spring MVC Controller pre process request body JSONObject 作为 Spring mvc 中的请求主体与球衣(杰克逊) - JSONObject as request body in Spring mvc with jersey(jackson) Spring MVC 强制请求空体 - Spring MVC enforce request empty body 在apache cxf中,我怎么知道SOAP请求消息是gzip压缩的? - In apache cxf, how do I know the SOAP request message is gzip compressed? 如何从Java发送压缩(gzip)JSON作为对Ajax请求的响应? - How can I send compressed (gzip) JSON as response to an Ajax Request, from Java? 如何使用gzip压缩读取HTTP响应? - How to read a HTTP response with gzip compressed? 如何在没有xml的Spring MVC中启用REST gzip压缩? - How to enable REST gzip compression in Spring MVC without xml?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM