Java 在Tomcat自定义阀中包装请求,以允许读取请求正文

Java 在Tomcat自定义阀中包装请求,以允许读取请求正文,java,tomcat,tomcat-valve,Java,Tomcat,Tomcat Valve,我被要求开发一个Tomcat阀门,它记录所有HTTP请求,包括它们的主体。由于包含主体的流只能读取一次,我发现我需要包装请求。我在这里找到了一个基于JBOSS的示例(下载链接“Maven project for a valve,the valve,the Dump the body的完整请求”): 我对它进行了调整,使之能够与香草Tomcat和更流行的API一起工作(我使用的是Tomcat catalina:8.5.20) 我的阀门是这样的: 公共类CaptureValve扩展了ValveBa

我被要求开发一个Tomcat阀门,它记录所有HTTP请求,包括它们的主体。由于包含主体的流只能读取一次,我发现我需要包装请求。我在这里找到了一个基于JBOSS的示例(下载链接“Maven project for a valve,the valve,the Dump the body的完整请求”):

我对它进行了调整,使之能够与香草Tomcat和更流行的API一起工作(我使用的是Tomcat catalina:8.5.20)

我的阀门是这样的:

公共类CaptureValve扩展了ValveBase{
// ...
@凌驾
公共void调用(请求、响应)抛出IOException、ServletException{
//包装请求,以便可以多次读取正文
RequestWrappedRequest=新的RequestWrapper(请求);
//重要信息-否则,请求不会在链的下游传递。。。
getNext().invoke(wrappedRequest,response);
//为了演示的目的而简化-现在我正在阅读正文来记录它
LogBody(wrappedRequest.getBody());
}
// ...
}
现在,正如您所想象的,
RequestWrapper
,只代理对包装对象的调用,除了
GetRequest
:下面是该类的相关部分:

公共类RequestWrapper扩展请求{
//...
公共RequestWrapper(请求包装)引发IOException{
wrappedCatalinaRequest=已包装;
loggingRequest=新的loggingRequest(已包装);
}    
//...
@凌驾
公共HttpServletRequest getRequest(){
//这里检索用于读取的实际请求
logger.info(“getRequest()”);
返回日志请求;
}
//...
}
因此,下一部分是
LoggingRequest
,它封装了内部
RequestFacade

私有类LoggingRequest扩展了RequestFacade{
私有日志输入流为;
LoggingRequest(请求请求)引发IOException{
超级(请求);
int len=0;
试一试{
len=Integer.parseInt(request.getHeader(“内容长度”);
}捕获(数字格式){
//忽略并假定长度为0
}
String contentType=request.getHeader(“内容类型”);
if(contentType!=null){
for(字符串ct:contentType.split(“;”)){
字符串s=ct.trim();
如果(s.startsWith(“字符集”)){
字符集=s.substring(s.indexOf(“=”)+1);
打破
}
}
}
//这一行导致了我在下面描述的问题
is=新的LoggingInputStream(request.getRequest().getInputStream(),len,charset);
}
@凌驾
公共ServletiInputStream getInputStream()引发IOException{
logger.info(“LoggingRequest.getInputStream()”;
回报是;
}
@凌驾
public BufferedReader getReader()引发IOException{
info(“LoggingRequest.getReader()”);
返回新的BufferedReader(新的InputStreamReader(is,字符集));
}
公共字符串getPayload(){
info(“方法:+newobject(){}.getClass().getEnclosuringMethod().getName());
返回值为.getPayload();
}
}
注意将输入流分配给
is
变量的行。这就是我下面描述的问题的起点

最后是
ServletInputStream
的包装器,正如您所看到的,其思想是在从实际的Tomcat应用程序读取主体时,将读取的字节也写入一个缓冲区,然后可以通过
getPayload()
方法再次读取该缓冲区。我剥离了代码的明显部分,如果您想查看所有详细信息,您可以在链接的示例项目中找到这些内容:

公共类LoggingInputStream扩展了ServletInputStream{
//...
公共日志输入流(ServletInputStream输入流,int长度,字符串字符集){
超级();
is=输入流;
字节=新的ByteArrayOutputStream(长度);
字符集名称=(字符集==null?“UTF-8”:字符集);
}
/*
*由于我们不确定将使用哪种方法,因此只需覆盖所有4种方法:
*/
@凌驾
public int read()引发IOException{
logger.info(“logginginInputStream.read()”;
int ch=is.read();
如果(ch!=-1){
字节。写入(ch);
//logger.info(“读取:+ch”);
//logger.info(“bytes.size()=”+bytes.size());
}
返回ch;
}
@凌驾
公共整数读取(字节[]b)引发IOException{
logger.info(“logginginInputStream.read(字节[]b)”;
//logger.info(“字节[].length=“+b.length”);
//logger.info(“字节[]=”+b);
int numbytes read=is.read(b);
如果(numBytesRead!=-1){
对于(int i=0;iRequestWrapper.<init> ctor RequestWrapper
RequestWrapper$LoggingRequest.<init> ctor LoggingRequest
LoggingInputStream.<init> LoggingInputStream length: 7
RequestWrapper.getContext Method: getContext
RequestWrapper.isAsyncSupported Method: isAsyncSupported
RequestWrapper.isAsync Method: isAsync
RequestWrapper.isAsyncDispatching Method: isAsyncDispatching
RequestWrapper.getRequest getRequest() - POST
RequestWrapper.getRequest getRequest() - POST
RequestWrapper.getUserPrincipal Method: getUserPrincipal
RequestWrapper.getSessionInternal Method: getSessionInternal
RequestWrapper.getWrapper Method: getWrapper
RequestWrapper.getRequestPathMB Method: getRequestPathMB
RequestWrapper.getMethod Method: getMethod
RequestWrapper.getMethod Method: getMethod
RequestWrapper.getUserPrincipal Method: getUserPrincipal
RequestWrapper.getNote Method: getNote
RequestWrapper.getCoyoteRequest Method: getCoyoteRequest
RequestWrapper.getCoyoteRequest Method: getCoyoteRequest
RequestWrapper.setAuthType Method: setAuthType
RequestWrapper.setUserPrincipal Method: setUserPrincipal
RequestWrapper.getSessionInternal Method: getSessionInternal
RequestWrapper.getContext Method: getContext
RequestWrapper.changeSessionId Method: changeSessionId
RequestWrapper.getPrincipal Method: getPrincipal
RequestWrapper.getRequestPathMB Method: getRequestPathMB
RequestWrapper.getWrapper Method: getWrapper
RequestWrapper.isAsyncSupported Method: isAsyncSupported
RequestWrapper.getRequestPathMB Method: getRequestPathMB
RequestWrapper.getDispatcherType Method: getDispatcherType
RequestWrapper.setAttribute Method: setAttribute
RequestWrapper.setAttribute Method: setAttribute
RequestWrapper.getFilterChain Method: getFilterChain
RequestWrapper.getAttribute Method: getAttribute
RequestWrapper.getAttribute Method: getAttribute
RequestWrapper.isAsyncDispatching Method: isAsyncDispatching
RequestWrapper.getRequest getRequest() - POST
RequestWrapper.getAttribute Method: getAttribute
RequestWrapper.isAsync Method: isAsync
RequestWrapper.getRequest getRequest() - POST
RequestWrapper.getBody Method: getBody
RequestWrapper$LoggingRequest.getPayload Method: getPayload
LoggingInputStream.getPayload getPayload size: 0
LoggingInputStream.getPayload getPayload result: