2013-05-19 99 views
7

我已經與如何gzip壓縮的請求主體在Spring MVC解碼

CONTENT-ENCODING deflate 

發送數據的客戶端我有這樣

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

目前「身體」代碼打印出亂碼,壓縮數據。 有什麼辦法可以讓Spring MVC自動解壓縮它嗎?

+2

只是一個猜測,但沒有彈簧支撐的HTTP過濾器? – SJuan76

回答

5

這應該由服務器,而不是應用程序來處理。

據我所知,Tomcat不支持它,儘管你可能會寫一個過濾器。

處理一個常見的方法就是把Tomcat的(或任何Java的容器,你使用),用於處理壓縮請求主體Apache服務器後面。

+0

這樣的:https://serverfault.com/questions/56700/is-it-possible-to-enable-http-compression-for-requests#answer-56707 – Adam

+0

如果比如你要以登錄到解壓消息?這種情況應該由應用程序配置來處理... – megalucio

13

您需要編寫自己的過濾器來解壓縮壓縮請求的正文。 正弦你會讀取整個輸入流的請求,你也需要重寫參數parcing方法。 這是我在代碼中使用的過濾器。僅支持gzip POST請求,但如果需要,您可以將其更新爲使用其他類型的請求。 另外注意的解析我用番石榴庫參數,你可以從這裏搶你的: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); 
        } 
       }); 
    } 
} 

}