我正在尝试访问Java Servlet过滤器中的两个http请求参数,这并不是什么新鲜事,但我惊讶地发现这些参数已经被使用了!正因为如此,它在过滤器链中不再可用。
似乎只有当参数出现在POST请求主体(例如,表单提交)中时,才会发生这种情况。
有没有办法读取参数而不使用它们?
到目前为止,我只找到了这个引用:Servlet Filter using request.getParameter loses Form data。
谢谢!
发布于 2016-08-26 04:33:48
我知道我来晚了,但这个问题对我来说仍然是相关的,所以这篇文章是谷歌最热门的帖子之一。我将继续发布我的解决方案,希望其他人可以节省几个小时。
在我的例子中,我需要用它们的主体记录所有的请求和响应。使用Spring Framework,答案实际上非常简单,只需使用ContentCachingRequestWrapper和ContentCachingResponseWrapper即可。
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
}
}
}
发布于 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。这并不是很好。
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();
}
}
发布于 2012-04-18 22:16:08
惟一的方法是您自己在过滤器中使用整个输入流,从中获取所需的内容,然后为您读取的内容创建一个新的InputStream,并将该InputStream放入ServletRequestWrapper (或HttpServletRequestWrapper)中。
缺点是您必须自己解析有效负载,该标准不会为您提供该功能。
附录--
正如我所说的,您需要查看HttpServletRequestWrapper。
在过滤器中,继续调用FilterChain.doFilter(request,response)。
对于普通筛选器,请求和响应与传入筛选器的请求和响应相同。这并不是必须的。您可以将这些替换为您自己的请求和/或响应。
HttpServletRequestWrapper就是专门为此而设计的。您将原始请求传递给它,然后就可以拦截所有的调用。您可以创建自己的子类,并用自己的方法替换getInputStream方法。您不能更改原始请求的输入流,因此您可以使用此包装器并返回您自己的输入流。
最简单的情况是将原始请求输入流消费到一个字节缓冲区,对它执行任何您想要的魔术,然后从该缓冲区创建一个新的ByteArrayInputStream。这是在您的包装器中返回的内容,它被传递给FilterChain.doFilter方法。
您需要子类化ServletInputStream并为您的ByteArrayInputStream创建另一个包装器,但这也不是什么大问题。
https://stackoverflow.com/questions/10210645
复制相似问题