Http Servlet request lose params from POST body after read it once

前端 未结 13 2103
一个人的身影
一个人的身影 2020-11-22 14:56

I\'m trying to accessing two http request parameters in a Java Servlet filter, nothing new here, but was surprised to find that the parameters have already been consumed! Be

13条回答
  •  隐瞒了意图╮
    2020-11-22 15:48

    The above answers were very helpful, but still had some problems in my experience. On tomcat 7 servlet 3.0, the getParamter and getParamterValues also had to be overwritten. The solution here includes both get-query parameters and the post-body. It allows for getting raw-string easily.

    Like the other solutions it uses Apache commons-io and Googles Guava.

    In this solution the getParameter* methods do not throw IOException but they use super.getInputStream() (to get the body) which may throw IOException. I catch it and throw runtimeException. It is not so nice.

    import com.google.common.collect.Iterables;
    import com.google.common.collect.ObjectArrays;
    
    import org.apache.commons.io.IOUtils;
    import org.apache.http.NameValuePair;
    import org.apache.http.client.utils.URLEncodedUtils;
    import org.apache.http.entity.ContentType;
    
    import java.io.BufferedReader;
    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.UnsupportedEncodingException;
    import java.nio.charset.Charset;
    import java.util.Collections;
    import java.util.LinkedHashMap;
    import java.util.List;
    import java.util.Map;
    
    import javax.servlet.ServletInputStream;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletRequestWrapper;
    
    /**
     * Purpose of this class is to make getParameter() return post data AND also be able to get entire
     * body-string. In native implementation any of those two works, but not both together.
     */
    public class MultiReadHttpServletRequest extends HttpServletRequestWrapper {
        public static final String UTF8 = "UTF-8";
        public static final Charset UTF8_CHARSET = Charset.forName(UTF8);
        private ByteArrayOutputStream cachedBytes;
        private Map parameterMap;
    
        public MultiReadHttpServletRequest(HttpServletRequest request) {
            super(request);
        }
    
        public static void toMap(Iterable inputParams, Map toMap) {
            for (NameValuePair e : inputParams) {
                String key = e.getName();
                String value = e.getValue();
                if (toMap.containsKey(key)) {
                    String[] newValue = ObjectArrays.concat(toMap.get(key), value);
                    toMap.remove(key);
                    toMap.put(key, newValue);
                } else {
                    toMap.put(key, new String[]{value});
                }
            }
        }
    
        @Override
        public ServletInputStream getInputStream() throws IOException {
            if (cachedBytes == null) cacheInputStream();
            return new CachedServletInputStream();
        }
    
        @Override
        public BufferedReader getReader() throws IOException {
            return new BufferedReader(new InputStreamReader(getInputStream()));
        }
    
        private void cacheInputStream() throws IOException {
        /* Cache the inputStream in order to read it multiple times. For
         * convenience, I use apache.commons IOUtils
         */
            cachedBytes = new ByteArrayOutputStream();
            IOUtils.copy(super.getInputStream(), cachedBytes);
        }
    
        @Override
        public String getParameter(String key) {
            Map parameterMap = getParameterMap();
            String[] values = parameterMap.get(key);
            return values != null && values.length > 0 ? values[0] : null;
        }
    
        @Override
        public String[] getParameterValues(String key) {
            Map parameterMap = getParameterMap();
            return parameterMap.get(key);
        }
    
        @Override
        public Map getParameterMap() {
            if (parameterMap == null) {
                Map result = new LinkedHashMap();
                decode(getQueryString(), result);
                decode(getPostBodyAsString(), result);
                parameterMap = Collections.unmodifiableMap(result);
            }
            return parameterMap;
        }
    
        private void decode(String queryString, Map result) {
            if (queryString != null) toMap(decodeParams(queryString), result);
        }
    
        private Iterable decodeParams(String body) {
            Iterable params = URLEncodedUtils.parse(body, UTF8_CHARSET);
            try {
                String cts = getContentType();
                if (cts != null) {
                    ContentType ct = ContentType.parse(cts);
                    if (ct.getMimeType().equals(ContentType.APPLICATION_FORM_URLENCODED.getMimeType())) {
                        List postParams = URLEncodedUtils.parse(IOUtils.toString(getReader()), UTF8_CHARSET);
                        params = Iterables.concat(params, postParams);
                    }
                }
            } catch (IOException e) {
                throw new IllegalStateException(e);
            }
            return params;
        }
    
        public String getPostBodyAsString() {
            try {
                if (cachedBytes == null) cacheInputStream();
                return cachedBytes.toString(UTF8);
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException(e);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    
        /* An inputStream which reads the cached request body */
        public class CachedServletInputStream extends ServletInputStream {
            private ByteArrayInputStream input;
    
            public CachedServletInputStream() {
                /* create a new input stream from the cached request body */
                input = new ByteArrayInputStream(cachedBytes.toByteArray());
            }
    
            @Override
            public int read() throws IOException {
                return input.read();
            }
        }
    
        @Override
        public String toString() {
            String query = dk.bnr.util.StringUtil.nullToEmpty(getQueryString());
            StringBuilder sb = new StringBuilder();
            sb.append("URL='").append(getRequestURI()).append(query.isEmpty() ? "" : "?" + query).append("', body='");
            sb.append(getPostBodyAsString());
            sb.append("'");
            return sb.toString();
        }
    }
    

提交回复
热议问题