在HandlerInterceptor中获取RequestBody和ResponseBody

时间:2014-01-17 18:38:32

标签: java spring spring-mvc

我必须实现的逻辑是将所有请求记录在一起提交给DB的主体。

所以我决定使用afterCompletion的{​​{1}}方法。 有两个参数传递给此方法HandlerInterceptorHttpServletRequest

问题是:如何从提供的对象中获取HttpServletResponseRequestBody

据我所知,我们可以使用ResponseBody@RequestBody。我可以在@ResponseBody上重复使用它们吗?

7 个答案:

答案 0 :(得分:8)

据我所知,RequestBodyResponseBody只能读取一次。所以你不应该在Interceptor中阅读它们。 这是一些explanation

答案 1 :(得分:4)

您可以扩展RequestBodyAdviceAdapter并实现方法afterBodyRead

@ControllerAdvice
public MyRequestBodyAdviceAdapter extends RequestBodyAdviceAdapter {

    @Override
    public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter,
            Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {

        // write body -your input dto- to logs or database or whatever

        return body;
    }

}

RequestBodyAdvice在HandlerInterceptor之前预先设置请求链。正是在http请求输入流转换为您的对象之后。

答案 2 :(得分:3)

这是一个相当古老的线索,但是为了寻找从Interceptor获取RequestBodyResponseBody的方法的人。以下是我的工作方式。

RequestBody,可以简单地使用IOUtils:

String requestBody= IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8);

ResponseBody,我不得不使用HttpServletResponseCopier:

ServletLoggingFilter.HttpServletResponseCopier copier = (ServletLoggingFilter.HttpServletResponseCopier) response;
String responseBody = new String(copier.getCopy());

答案 3 :(得分:2)

这是来自HandlerInterceptor javadoc的javadoc的相关声明。

  

完成请求处理后回调,即渲染视图后回调。将在处理程序执行的任何结果上调用,从而允许适当的资源清理。

HandlerIntercepter Javadoc

您无法访问请求正文(作为InputStream),因为已经读取了请求。如果需要访问请求参数,可以通过调用 - request.getParameter(“parameterName”);

使用请求对象来访问

您无法访问响应正文,因为已经呈现了响应。这意味着响应已经提交给输出流。

答案 4 :(得分:2)

You may use ResponseBodyAdvice available since Spring 4.1, with which you could intercept the response body before the body is written to the client.

Here is an example: https://sdqali.in/blog/2016/06/08/filtering-responses-in-spring-mvc/

答案 5 :(得分:1)

您可以使用以下方法从HandlerInterceptorAdapter方法中的HTTPServlet请求中获取请求正文:

request.getInputStream();

如果您希望它与扫描仪一起使用,您可以按如下方式分配扫描仪:

Scanner scanner = new Scanner(request.getInputStream(), "UTF-8");

然后,您可以使用某些操作或扫描仪技巧从扫描仪中使用所需的数据。 或者您也可以使用Apache Commons IO将其分配给字符串:

String requestStr = IOUtils.toString(request.getInputStream());

答案 6 :(得分:-1)

正如其他人所说,您不能多次读取请求输入流或响应输出流,但是可以使用过滤器将原始的请求和响应对象替换为包装的对象。 这样,您可以实现包装程序并缓冲有效负载,从而可以按需要多次使用它。

您需要的是: 创建一个过滤器:

@Component
public class RequestFilter implements Filter {
    @Autowired      
    LogApiInterceptor logApiInterceptor;
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                    throws IOException, ServletException {
        ...
            // LOG REQUEST
            ResettableStreamHttpServletRequest wrappedRequest = null;
            ResettableStreamHttpServletResponse wrappedResponse = null;
            try {
                    wrappedRequest = new ResettableStreamHttpServletRequest((HttpServletRequest) request);
                    wrappedResponse = new ResettableStreamHttpServletResponse((HttpServletResponse) response);
                    logApiInterceptor.writeRequestPayloadAudit(wrappedRequest);
            } catch (Exception e) {
                    LOG.error("Fail to wrap request and response",e);
            }
            try {
                    chain.doFilter(wrappedRequest, wrappedResponse);
            } finally {
                    MDC.clear();
            }
        ...
    }
}

注册您的拦截器:

@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
        @Override
        public void addInterceptors(InterceptorRegistry registry){
                registry.addInterceptor(new LogApiInterceptor());
        }
}

创建拦截器:

public class LogApiInterceptor extends HandlerInterceptorAdapter {
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            if ( response instanceof ResettableStreamHttpServletResponse ) {
                    ((ResettableStreamHttpServletResponse)response).payloadFilePrefix = ((ResettableStreamHttpServletRequest)request).payloadFilePrefix;
                    ((ResettableStreamHttpServletResponse)response).payloadTarget  = ((ResettableStreamHttpServletRequest)request).payloadTarget;
                    writeResponsePayloadAudit((ResettableStreamHttpServletResponse) response);
            }
    }
    public String getRawHeaders(HttpServletRequest request) {

            StringBuffer rawHeaders = new StringBuffer();
            Enumeration headerNames = request.getHeaderNames();
            while (headerNames.hasMoreElements()) {
                    String key = (String) headerNames.nextElement();
                    String value = request.getHeader(key);
                    rawHeaders.append(key).append(":").append(value).append("\n");
            }

            return rawHeaders.toString();
    }
    public String getRawHeaders(HttpServletResponse response){
            StringBuffer rawHeaders = new StringBuffer();
            Enumeration headerNames = Collections.enumeration(response.getHeaderNames());
            while (headerNames.hasMoreElements()) {
                    String key = (String) headerNames.nextElement();
                    String value = response.getHeader(key);
                    rawHeaders.append(key).append(":").append(value).append("\n");
            }

            return rawHeaders.toString();
    }
    private void writePayloadAudit(String payloadFile, String rawHeaders, String requestBody) throws IOException {
            try (Writer writer = new BufferedWriter(
                            new OutputStreamWriter(new FileOutputStream(payloadFile), StandardCharsets.UTF_8))) {
                    writer.write(rawHeaders);
                    writer.write("\n");
                    writer.write(requestBody);
            }
    }
}

RessetableInputStream:

public class ResettableServletInputStream extends ServletInputStream {
        public InputStream inputStream;
        private ServletInputStream servletInputStream = new ServletInputStream(){
                boolean isFinished = false;
                boolean isReady = true;
                ReadListener readListener = null;
                public int read() throws IOException {
                        int i = inputStream.read();
                        isFinished = i == -1;
                        isReady = !isFinished;
                        return i;
                }

                @Override
                public boolean isFinished() {
                        return isFinished;
                }

                @Override
                public boolean isReady() {
                        return isReady;
                }

                @Override
                public void setReadListener(ReadListener readListener) {
                        this.readListener = readListener;
                }
        };

        public int available() throws IOException {
                return inputStream.available();
        }

        public void close() throws IOException {
                inputStream.close();
        }

        public void mark(int readLimit){
                inputStream.mark(readLimit);
        }

        public boolean markSupported(){
                return inputStream.markSupported();
        }

        public int read(byte[] b, int off, int len) throws IOException {
                return inputStream.read(b,off,len);
        }

        public int readline(byte[] b, int off, int len) throws IOException {
                if ( len <= 0 ){
                        return 0;
                }

                int count=0,c;

                while ( (c = read()) != -1 ) {
                        b[off++] = (byte) c;
                        count++;
                        if ( c == '\n' || count == len ) {
                                break;
                        }
                }

                return count > 0 ? count : -1;
        }

        public void reset() throws IOException {
                inputStream.reset();
        }

        public long skip(long n) throws IOException {
                return inputStream.skip(n);
        }

        @Override
        public int read() throws IOException {
                return inputStream.read();
        }

        @Override
        public void setReadListener(ReadListener readListener){
                servletInputStream.setReadListener(readListener);
        }

        public boolean isReady() {
                return servletInputStream.isReady();
        }

        public boolean isFinished() {
                return servletInputStream.isFinished();
        }
}

ResettableOutputStream

public class ResettableServletOutputStream extends ServletOutputStream {

        @Autowired
        LogApiInterceptor logApiInterceptor;

        public OutputStream outputStream;
        private ResettableStreamHttpServletResponse wrappedResponse;
        private ServletOutputStream servletOutputStream = new ServletOutputStream(){
                boolean isFinished = false;
                boolean isReady = true;
                WriteListener writeListener = null;

                @Override
                public void setWriteListener(WriteListener writeListener) {
                        this.writeListener = writeListener;
                }

                public boolean isReady(){
                        return isReady;
                }
                @Override
                public void write(int w) throws IOException{
                        outputStream.write(w);
                        wrappedResponse.rawData.add(new Integer(w).byteValue());
                }
        };

        public ResettableServletOutputStream(ResettableStreamHttpServletResponse wrappedResponse) throws IOException {
                this.outputStream = wrappedResponse.response.getOutputStream();
                this.wrappedResponse = wrappedResponse;
        }               

        @Override
        public void close() throws IOException {
                LOG.info("** RESPONSE CLOSE **");
                outputStream.close();
                logApiInterceptor.writeResponsePayloadAudit(wrappedResponse);
        }

        @Override
        public void setWriteListener(WriteListener writeListener) {
                servletOutputStream.setWriteListener( writeListener );
        }
        @Override
        public boolean isReady(){
                return servletOutputStream.isReady();
        }

        @Override
        public void write(int w) throws IOException {
                servletOutputStream.write(w);
        }
}

RequestWrapper

public class ResettableStreamHttpServletRequest extends HttpServletRequestWrapper {

        private byte[] rawData = {};
        private HttpServletRequest request;
        private ResettableServletInputStream servletStream;

        public String requestId;
        public String payloadFilePrefix;
        public String payloadTarget;

        ResettableStreamHttpServletRequest(HttpServletRequest request) throws IOException {
                super(request);
                this.request = request;
                this.servletStream = new ResettableServletInputStream();
        }

        void resetInputStream() throws IOException {
                initRawData();
                servletStream.inputStream = new ByteArrayInputStream(rawData);
        }

        private void initRawData() throws IOException {
                if ( rawData.length == 0 ) {
                        byte[] b = IOUtils.toByteArray(this.request.getInputStream());
                        if ( b != null )
                                rawData = b;
                }
                servletStream.inputStream = new ByteArrayInputStream(rawData);
        }
        @Override
        public ServletInputStream getInputStream() throws IOException {
                initRawData();
                return servletStream;
        }
        public BufferedReader getReader() throws IOException {
                initRawData();
                String encoding = getCharacterEncoding();
                if ( encoding != null ) {
                        return new BufferedReader(new InputStreamReader(servletStream, encoding));
                } else {
                        return new BufferedReader(new InputStreamReader(servletStream));
                }
        }
}

ResponseWrapper

public class ResettableStreamHttpServletResponse extends HttpServletResponseWrapper {

        public String requestId;
        public String payloadFilePrefix; 
        public String payloadTarget;

        public List<Byte> rawData = new ArrayList<Byte>();
        public HttpServletResponse response;
        private ResettableServletOutputStream servletStream;

        ResettableStreamHttpServletResponse(HttpServletResponse response) throws IOException {
                super(response);
                this.response = response;
                this.servletStream = new ResettableServletOutputStream(this);
        }       

        @Override
        public ServletOutputStream getOutputStream() throws IOException {
                return servletStream;
        }       
        public PrintWriter getWriter() throws IOException {
                String encoding = getCharacterEncoding();
                if ( encoding != null ) {
                        return new PrintWriter(new OutputStreamWriter(servletStream, encoding));
                } else {
                        return new PrintWriter(new OutputStreamWriter(servletStream));
                }
        }
}