Java 记录HttpRequest参数和请求正文
我正在尝试为我的web应用创建请求日志。我正在使用Spring3。 0 我实现了一个扩展类Java 记录HttpRequest参数和请求正文,java,spring-mvc,httprequest,Java,Spring Mvc,Httprequest,我正在尝试为我的web应用创建请求日志。我正在使用Spring3。 0 我实现了一个扩展类HandlerInterceptorAdapter,并使用preHandle(HttpServletRequest、HttpServletResponse、Object handler)拦截请求 在该方法中,我希望能够记录请求主体(我的参数是直接写入请求主体的XML对象),为此我使用request.getReader() 问题是-稍后,当spring控制器尝试读取请求时,我将得到一个IllegalState
HandlerInterceptorAdapter
,并使用preHandle(HttpServletRequest、HttpServletResponse、Object handler)
拦截请求
在该方法中,我希望能够记录请求主体(我的参数是直接写入请求主体的XML对象),为此我使用request.getReader()代码>
问题是-稍后,当spring控制器尝试读取请求时,我将得到一个IllegalStateException
有什么方法可以达到我的目的吗?你可以用过滤器来实现。请求参数很容易处理。
然而,与请求机构打交道将困难得多
并且需要包装servlet请求,请参见:
您需要查看传入请求的大小,并决定是否将请求正文存储为tmp文件或字符串
您需要使用用于日志记录的文件或保存的字符串重写ServetRequest.getInputStream()
如果请求主体很大,我建议将输入流放入缓冲输入流,然后读取主体的开头
public class LogRequest extends HttpServletRequestWrapper {
public LogRequest(HttpServletRequest request) {
super(request);
}
@Override
public ServletInputStream getInputStream() throws IOException {
//read from tmp file or string.
}
@Override
public BufferedReader getReader() throws IOException {
//read from tmp file or string
}
}
Spring有一个现成的过滤器可以为您实现这一点——请参阅中描述的AbstractRequestLoggingFilter
及其子类的用法
请注意,使用此解决方案时,只有在请求处理完成且应用程序已读取请求正文后,才会记录请求正文。小请求的简单实现。不要将其用于多部分请求
package ru.rbs.logger.web;
import org.apache.commons.io.IOUtils;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
class CachedRequestWrapper extends HttpServletRequestWrapper {
private final byte[] cachedBody;
CachedRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
IOUtils.copy(request.getInputStream(), bos);
cachedBody = bos.toByteArray();
}
@Override
public ServletInputStream getInputStream() throws IOException {
return new CachedServletInputStream();
}
byte[] toByteArray(){
return cachedBody;
}
private class CachedServletInputStream extends ServletInputStream {
private InputStream baseInputStream;
CachedServletInputStream() throws IOException {
baseInputStream = new ByteArrayInputStream(cachedBody);
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() throws IOException {
return baseInputStream.read();
}
}
}
有几个问题。在ServletRequest中,只能调用getReader()或getInputStream(),不能同时调用两者。如果你两个都打电话,你会得到一个非法州例外。您可以尝试调用getInputStream,但如果读取输入流,Spring可能看不到它,仍然可能会出现错误。(ServletInputStream可能支持重置,但我不这么认为)。最好的办法是在XML反序列化过程中记录参数(您应该知道是哪个类执行此操作)或者紧接着。您是否考虑过将http服务器配置为记录请求头或使用servlet过滤器进行记录?@happymeal我不需要仅记录头,我也需要正文。任何一种解决方案都仍然有效。在servlet过滤器方法中,您只需读取输入流并打印出所有内容;这也将包括请求主体。在http服务器方法中,您必须参考您的文档,因为不同供应商的配置不同。包装请求仍然不允许我调用getReader()
两次。我仍然得到illegalStateExceptionNoam Nevo您要对原始请求调用输入方法(reader/inputstream),然后用请求包装器包装原始请求,并覆盖getReader()和friends。AbstractRequestLoggingFilter不允许您在处理请求之前记录请求。您无法在AbstractRequestLoggingFilter#beforeRequest中获取有效负载。如果对您合适,您可以覆盖AbstractRequestLoggingFilter#afterRequest,它将记录包括有效负载在内的所有内容。