首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >Http Servlet请求在读取一次后从POST正文中丢失参数

Http Servlet请求在读取一次后从POST正文中丢失参数
EN

Stack Overflow用户
提问于 2012-04-18 21:40:50
回答 10查看 135.5K关注 0票数 94

我正在尝试访问Java Servlet过滤器中的两个http请求参数,这并不是什么新鲜事,但我惊讶地发现这些参数已经被使用了!正因为如此,它在过滤器链中不再可用。

似乎只有当参数出现在POST请求主体(例如,表单提交)中时,才会发生这种情况。

有没有办法读取参数而不使用它们?

到目前为止,我只找到了这个引用:Servlet Filter using request.getParameter loses Form data

谢谢!

EN

回答 10

Stack Overflow用户

发布于 2016-08-26 04:33:48

我知道我来晚了,但这个问题对我来说仍然是相关的,所以这篇文章是谷歌最热门的帖子之一。我将继续发布我的解决方案,希望其他人可以节省几个小时。

在我的例子中,我需要用它们的主体记录所有的请求和响应。使用Spring Framework,答案实际上非常简单,只需使用ContentCachingRequestWrapperContentCachingResponseWrapper即可。

代码语言:javascript
复制
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class LoggingFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void destroy() {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper((HttpServletRequest) request);
        ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper((HttpServletResponse) response);

        try {
            chain.doFilter(requestWrapper, responseWrapper);
        } finally {

            String requestBody = new String(requestWrapper.getContentAsByteArray());
            String responseBody = new String(responseWrapper.getContentAsByteArray());
            // Do not forget this line after reading response content or actual response will be empty!
            responseWrapper.copyBodyToResponse();

            // Write request and response body, headers, timestamps etc. to log files

        }

    }

}
票数 39
EN

Stack Overflow用户

发布于 2016-04-14 18:09:38

上面的答案非常有用,但在我的经验中仍然存在一些问题。在Tomcat7Servlet3.0上,getParamter和getParamterValues也必须被覆盖。这里的解决方案包括get-query参数和post-body。它允许很容易地获取原始字符串。

与其他解决方案一样,它使用Apache commons-io和Googles Guava。

在这个解决方案中,getParameter*方法没有抛出IOException,但它们使用了super.getInputStream() (获取主体),这可能会抛出IOException。我接住它并抛出runtimeException。这并不是很好。

代码语言:javascript
复制
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();
    }
}
票数 7
EN

Stack Overflow用户

发布于 2012-04-18 22:16:08

惟一的方法是您自己在过滤器中使用整个输入流,从中获取所需的内容,然后为您读取的内容创建一个新的InputStream,并将该InputStream放入ServletRequestWrapper (或HttpServletRequestWrapper)中。

缺点是您必须自己解析有效负载,该标准不会为您提供该功能。

附录--

正如我所说的,您需要查看HttpServletRequestWrapper。

在过滤器中,继续调用FilterChain.doFilter(request,response)。

对于普通筛选器,请求和响应与传入筛选器的请求和响应相同。这并不是必须的。您可以将这些替换为您自己的请求和/或响应。

HttpServletRequestWrapper就是专门为此而设计的。您将原始请求传递给它,然后就可以拦截所有的调用。您可以创建自己的子类,并用自己的方法替换getInputStream方法。您不能更改原始请求的输入流,因此您可以使用此包装器并返回您自己的输入流。

最简单的情况是将原始请求输入流消费到一个字节缓冲区,对它执行任何您想要的魔术,然后从该缓冲区创建一个新的ByteArrayInputStream。这是在您的包装器中返回的内容,它被传递给FilterChain.doFilter方法。

您需要子类化ServletInputStream并为您的ByteArrayInputStream创建另一个包装器,但这也不是什么大问题。

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/10210645

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档