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

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<String, String[]> parameterMap;

    public MultiReadHttpServletRequest(HttpServletRequest request) {
        super(request);
    }

    public static void toMap(Iterable<NameValuePair> inputParams, Map<String, String[]> 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<String, String[]> parameterMap = getParameterMap();
        String[] values = parameterMap.get(key);
        return values != null && values.length > 0 ? values[0] : null;
    }

    @Override
    public String[] getParameterValues(String key) {
        Map<String, String[]> parameterMap = getParameterMap();
        return parameterMap.get(key);
    }

    @Override
    public Map<String, String[]> getParameterMap() {
        if (parameterMap == null) {
            Map<String, String[]> result = new LinkedHashMap<String, String[]>();
            decode(getQueryString(), result);
            decode(getPostBodyAsString(), result);
            parameterMap = Collections.unmodifiableMap(result);
        }
        return parameterMap;
    }

    private void decode(String queryString, Map<String, String[]> result) {
        if (queryString != null) toMap(decodeParams(queryString), result);
    }

    private Iterable<NameValuePair> decodeParams(String body) {
        Iterable<NameValuePair> 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<NameValuePair> 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();
    }
}

Examples related to java

Under what circumstances can I call findViewById with an Options Menu / Action Bar item? How much should a function trust another function How to implement a simple scenario the OO way Two constructors How do I get some variable from another class in Java? this in equals method How to split a string in two and store it in a field How to do perspective fixing? String index out of range: 4 My eclipse won't open, i download the bundle pack it keeps saying error log

Examples related to servlets

Google Recaptcha v3 example demo Difference between request.getSession() and request.getSession(true) init-param and context-param java.lang.NoClassDefFoundError: org/json/JSONObject how to fix Cannot call sendRedirect() after the response has been committed? getting error HTTP Status 405 - HTTP method GET is not supported by this URL but not used `get` ever? Create a simple Login page using eclipse and mysql Spring get current ApplicationContext insert data into database using servlet and jsp in eclipse What is WEB-INF used for in a Java EE web application?

Examples related to parameters

Stored procedure with default parameters AngularJS ui router passing data between states without URL C#: HttpClient with POST parameters HTTP Request in Swift with POST method In Swift how to call method with parameters on GCD main thread? How to pass parameters to maven build using pom.xml? Default Values to Stored Procedure in Oracle How do you run a .exe with parameters using vba's shell()? How to set table name in dynamic SQL query? How to pass parameters or arguments into a gradle task

Examples related to httprequest

Http post and get request in angular 6 Postman: How to make multiple requests at the same time Adding header to all request with Retrofit 2 Understanding Chrome network log "Stalled" state Why is this HTTP request not working on AWS Lambda? Simulate a specific CURL in PostMan HTTP Request in Swift with POST method What are all the possible values for HTTP "Content-Type" header? How to get host name with port from a http or https request Why I get 411 Length required error?

Examples related to servlet-filters

How to add a filter class in Spring Boot? error: package javax.servlet does not exist Giving multiple URL patterns to Servlet Filter Http Servlet request lose params from POST body after read it once How to define servlet filter order of execution using annotations in WAR How to redirect in a servlet filter? How can I get the request URL from a Java Filter? Can I exclude some concrete urls from <url-pattern> inside <filter-mapping>? Adding an HTTP Header to the request in a servlet filter How to use a servlet filter in Java to change an incoming servlet request url?