Java 在 HandlerInterceptor 处获取 RequestBody 和 ResponseBody

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21193380/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 07:12:55  来源:igfitidea点击:

Get RequestBody and ResponseBody at HandlerInterceptor

javaspringspring-mvc

提问by kyberorg

Logic I have to implement is logging all requests with body served to DB.

我必须实现的逻辑是记录所有请求,并将正文提供给 DB。

So I decided to use: afterCompletionmethod of HandlerInterceptor. There are two parameters passed to this method HttpServletRequestand HttpServletResponseamong the others.

所以我决定使用:afterCompletion方法HandlerInterceptor。有两个参数传递给此方法HttpServletRequest以及HttpServletResponse其他参数。

Question is: how to get RequestBodyand ResponseBodyfrom supplied objects?

问题是:如何获取RequestBodyResponseBody获取提供的对象?

As far as I know at Controller we can use @RequestBodyand @ResponseBody. Can I reuse them at HandlerInterceptor?

据我所知,我们可以在 Controller 中使用@RequestBodyand @ResponseBody。我可以重用它们HandlerInterceptor吗?

采纳答案by simonmarcell

As far as I know, RequestBodyand ResponseBodycan be read only once. So you should not read them in an Interceptor. Here's some explanation.

据我所知,RequestBody并且ResponseBody只能读取一次。所以你不应该在一个Interceptor. 这里有一些解释

回答by Sashi

Here's the relevant statement from javadoc for HandlerInterceptor javadoc.

这是 HandlerInterceptor javadoc 的 javadoc 中的相关声明。

Callback after completion of request processing, that is, after rendering the view. Will be called on any outcome of handler execution, thus allows for proper resource cleanup.

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

HandlerIntercepter Javadoc

HandlerIntercepter Javadoc

You cannot access the request body (as an InputStream) because the request was already read. If you need access to request parameters, you could do so using the request object by calling - request.getParameter("parameterName");

您无法访问请求正文(作为 InputStream),因为该请求已被读取。如果您需要访问请求参数,您可以通过调用请求对象来实现 - request.getParameter("parameterName");

You cannot access the response body because the response is already rendered. That means the response is already committed to the outputstream.

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

回答by Aarya

You can get the request body from HTTPServlet request inside HandlerInterceptorAdapter methods using the method:

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

request.getInputStream();

If you want it to use with scanner you can assign the scanner as follows:

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

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

Then you can use the required data from the scanner using some manipulation or scanner tricks. Or else you can assign it to a string also using Apache Commons IO:

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

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

回答by TorIsHere

This is quite an old thread but for the sake of people who looking for a way to get RequestBodyand ResponseBodyfrom Interceptor. Here is how I got it working.

这是一个相当古老的线程,但为了那些正在寻找一种方式来获取RequestBodyResponseBody从 Interceptor 的人。这是我如何让它工作的。

RequestBody, can simply use IOUtils:

RequestBody, 可以简单地使用 IOUtils:

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

ResponseBody, I had to used HttpServletResponseCopier:

ResponseBody,我不得不使用 HttpServletResponseCopier:

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

回答by Mingjiang Shi

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

您可以使用ResponseBodyAdvice自 Spring 4.1 以来的可用,您可以在将主体写入客户端之前拦截响应主体。

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

这是一个例子:https: //sdqali.in/blog/2016/06/08/filtering-responses-in-spring-mvc/

回答by ton

As others said, you can not read request input stream or response output stream more than once, but, you can use Filters to replace the original request and response objects with wrapped ones. This way you can implement your wrapper and bufferize the payload, this way you can use it how many times you want.

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

Here a repo with the working code: https://github.com/glaudiston/spring-boot-rest-payload-logging

这是一个带有工作代码的回购:https: //github.com/glaudiston/spring-boot-rest-payload-logging

Just do a mvn clean installon it and be happy.

做一个mvn clean install就可以了,开心就好。

$ java -jar target/rest-service-0.0.1-SNAPSHOT.jar                                                                                                                                                                                                                               

  .   ____          _            __ _ _
 /\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.2.RELEASE)

2020-01-24 13:06:07.297  INFO 918 --- [           main] c.e.restservice.RestServiceApplication   : Starting RestServiceApplication v0.0.1-SNAPSHOT on ca275nt with PID 918 (/home/ggs/src/spring-boot-rest-payload-logging/target/rest-service-0.0.1-SNAPSHOT.jar started by ggs
in /home/ggs/src/spring-boot-rest-payload-logging)
2020-01-24 13:06:07.301  INFO 918 --- [           main] c.e.restservice.RestServiceApplication   : No active profile set, falling back to default profiles: default                                                                                                             
2020-01-24 13:06:08.331  INFO 918 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)                                                                                                                                 
2020-01-24 13:06:08.348  INFO 918 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2020-01-24 13:06:08.348  INFO 918 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.29]                                                                                                                              
2020-01-24 13:06:08.410  INFO 918 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext                                                                                                                           
2020-01-24 13:06:08.410  INFO 918 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1044 ms                                                                                                              
2020-01-24 13:06:08.627  INFO 918 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'                                                                                                                       
2020-01-24 13:06:08.787  INFO 918 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''                                                                                                                  
2020-01-24 13:06:08.791  INFO 918 --- [           main] c.e.restservice.RestServiceApplication   : Started RestServiceApplication in 1.928 seconds (JVM running for 2.319)                                                                                                      
2020-01-24 13:06:11.014  INFO 918 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'                                                                                                                    
2020-01-24 13:06:11.014  INFO 918 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'                                                                                                                                     
2020-01-24 13:06:11.022  INFO 918 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 8 ms
2020-01-24 13:06:11 [] INFO  RequestFilter:23 - doFilter, parsing request
2020-01-24 13:06:11 [] INFO  LogApiInterceptor:64 - Request Method: POST
2020-01-24 13:06:11 [] INFO  LogApiInterceptor:65 - Request Headers:
2020-01-24 13:06:11 [] INFO  LogApiInterceptor:66 - host:localhost:8080
user-agent:curl/7.64.0
accept:*/*
content-length:32
content-type:application/x-www-form-urlencoded

2020-01-24 13:06:11 [] INFO  LogApiInterceptor:67 - Request body:
2020-01-24 13:06:11 [] INFO  LogApiInterceptor:68 - testdata=123456789&test2=9876543
2020-01-24 13:06:11 [] INFO  LogApiInterceptor:75 - Response Status: 200
2020-01-24 13:06:11 [] INFO  LogApiInterceptor:76 - Response Headers:
2020-01-24 13:06:11 [] INFO  LogApiInterceptor:77 -
2020-01-24 13:06:11 [] INFO  LogApiInterceptor:78 - Response body:
2020-01-24 13:06:11 [] INFO  LogApiInterceptor:84 - {"id":1,"content":"Hello, World!"}
──────────────────────────────────────────────────────────────────────────────────────────────────────────────
$ curl -X POST --data "testdata=123456789&test2=9876543" http://localhost:8080/greeting
{"id":1,"content":"Hello, World!"}

回答by Muhammad Hewedy

You can extend RequestBodyAdviceAdapterand implement the method afterBodyRead:

您可以扩展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;
    }

}

The RequestBodyAdvice comes in pre setp the request chain before the HandlerInterceptor. it is exactly after the http request inputstream is converted to your object.

RequestBodyAdvice 在HandlerInterceptor. 正是在 http 请求输入流转换为您的对象之后。