[英]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.