如何使用servlet过滤器记录请求和响应,以保留请求主体和响应主体? [英] How to log request and response using servlet filter preserving the request body and response body?
问题描述
我正在尝试在应用程序中记录每个传入请求和传出响应.我正在使用jee 6,所以我没有ContainerRequestFilter和ContainerResponseFilter类.所以我决定使用过滤器.
I am trying to log every incoming request and outgoing response in my application. I am using jee 6 so I do not have ContainerRequestFilter and ContainerResponseFilter classes. So I decided to use Filter.
我用@WebFilter("/*")注释了一个类,并实现了Filter接口.我成功读取了请求标头和请求正文.遇到一些困难,我还阅读了响应标头和响应正文.下面是一个代码段
I annotated a class with @WebFilter("/*") and implemented Filter interface. I successfully read the request headers and request body. With some difficulty I also read the response headers and response body. Below is a code snippet
MyHttpServletResponseWrapper wrapper = new MyHttpServletResponseWrapper((HttpServletResponse) response);
chain.doFilter(request, wrapper);
MyHttpServletResponseWrapper类
MyHttpServletResponseWrapper class
public class MyHttpServletResponseWrapper extends HttpServletResponseWrapper {
private StringWriter sw = new StringWriter();
public MyHttpServletResponseWrapper(HttpServletResponse response) { super(response); }
public PrintWriter getWriter() { return new PrintWriter(sw); }
public ServletOutputStream getOutputStream() {
return new ServletOutputStream (){
public void write(int B) { sw.append((char) B); }
};
}
public String getCopy() { sw.toString(); }
}
记录响应后,我会将响应写回到输出流,以便客户端可以接收响应.下面是代码
After logging the response I am writing the response back to the output stream so that the client can receive the response. Below is the code
logResponse(wrapper);
response.getOutputStream().write(wrapper.getCopy().getBytes());
我无法理解的是,如何在读取请求后将请求正文放回输入流中.
What I am not able to understand is, how to put back the request body in the input stream after reading it.
在诸如Jersey之类的标准API中,有一种方便的方法可以使用setEntityInputStream(inputStream)将其放回原处.
In standard APIs like the Jersey, there is a convenient way to put it back using setEntityInputStream(inputStream)
如何使用标准Java ServletOutputStream API做到这一点.
How do I do it with the standard Java ServletOutputStream api.
我不打算重新发明轮子.我试图避免使用Jersey,以便我的代码可以轻松迁移到新的jee版本.我也想了解像Jersey这样的API是如何做到的.
I am not trying to reinvent the wheel. I am trying to avoid using Jersey so that my code can be easily migrated to new jee versions. Also I would like to understand how APIs like Jersey does it.
要阅读正文的回复,请阅读以下链接,但它对我不起作用.容器抛出一个异常,说作家已经获得.
To read the response from the body I read the below link but it did not work for me. Container threw an exception saying writer already obtained.
P.S.
我想我已经弄清楚了如何设置输入流.下面是一些代码
I think I figured out how to set the input stream back. Below is some code
MyRequestWrapper requestwrapper = new MyRequestWrapper((HttpServletRequest) request);
chain.doFilter(requestwrapper,wrapper);
chain.doFilter(requestwrapper, wrapper);
MyRequestWrapper类
MyRequestWrapper class
public class MyRequestWrapper extends HttpServletRequestWrapper {
String body;
int counter;
public MyRequestWrapper(HttpServletRequest request) { super(request); }
public void setBody(String body) { this.body = body;}
public ServletInputStream getInputStream() {
return new ServletInputStream() {
public interest read() throws IOException {
if(counter >=body.length()) return -1;
return body.charAt(counter++);
}
};
}
}
我知道重写的getInputStream和getOutputStream中的代码质量不是很好.现在,我对此并不担心.我想知道这是一个正确的主意吗?如果那样的话,我想专注于代码质量.
I know that the quality of the code in overrided getInputStream and getOutputStream are not so great. Right now I am not worried about that. I want to know if this is a right idea? If then then I would like to concentrate on the code quality.
推荐答案
/** I know its too late but it can be helpfull for others as well. **//
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;
public class ApiIOLoggerFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) {}
@Override
public void destroy() {}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (request instanceof HttpServletRequest && response instanceof HttpServletResponse) {
ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper((HttpServletRequest) request);
ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper((HttpServletResponse) response);
try {
chain.doFilter(requestWrapper, responseWrapper);
} finally {
performRequestAudit(requestWrapper);
performResponseAudit(responseWrapper);
}
} else {
chain.doFilter(request, response);
}
}
private void performRequestAudit(ContentCachingRequestWrapper requestWrapper) {
if (requestWrapper != null && requestWrapper.getContentAsByteArray() != null && requestWrapper.getContentAsByteArray().length > 0) {
logger.info("\n Headers:: {} \n Request Body:: {}", new ServletServerHttpRequest((HttpServletRequest)requestWrapper.getRequest()).getHeaders(),
getPayLoadFromByteArray(requestWrapper.getContentAsByteArray(), requestWrapper.getCharacterEncoding()));
}
}
private void performResponseAudit(ContentCachingResponseWrapper responseWrapper)
throws IOException {
if (responseWrapper != null && responseWrapper.getContentAsByteArray() != null
&& responseWrapper.getContentAsByteArray().length > 0) {
logger.info("Reponse Body:: {}", getPayLoadFromByteArray(responseWrapper.getContentAsByteArray(),
responseWrapper.getCharacterEncoding()));
} else {
performErrorResponseAudit(responseWrapper);
}
responseWrapper.copyBodyToResponse();
}
private void performErrorResponseAudit(ContentCachingResponseWrapper responseWrapper) {
logger.warn("HTTP Error Satus Code::" + responseWrapper.getStatus());
}
private String getPayLoadFromByteArray(byte[] requestBuffer, String charEncoding) {
String payLoad = "";
try {
payLoad = new String(requestBuffer, charEncoding);
} catch (UnsupportedEncodingException unex) {
payLoad = "Unsupported-Encoding";
}
return payLoad;
}
}
这篇关于如何使用servlet过滤器记录请求和响应,以保留请求主体和响应主体?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!