Java 记录HttpRequest参数和请求正文

Java 记录HttpRequest参数和请求正文,java,spring-mvc,httprequest,Java,Spring Mvc,Httprequest,我正在尝试为我的web应用创建请求日志。我正在使用Spring3。 0 我实现了一个扩展类HandlerInterceptorAdapter,并使用preHandle(HttpServletRequest、HttpServletResponse、Object handler)拦截请求 在该方法中,我希望能够记录请求主体(我的参数是直接写入请求主体的XML对象),为此我使用request.getReader() 问题是-稍后,当spring控制器尝试读取请求时,我将得到一个IllegalState

我正在尝试为我的web应用创建请求日志。我正在使用Spring3。 0

我实现了一个扩展类
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,它将记录包括有效负载在内的所有内容。