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
Get RequestBody and ResponseBody at HandlerInterceptor
提问by kyberorg
Logic I have to implement is logging all requests with body served to DB.
我必须实现的逻辑是记录所有请求,并将正文提供给 DB。
So I decided to use: afterCompletion
method of HandlerInterceptor
.
There are two parameters passed to this method HttpServletRequest
and HttpServletResponse
among the others.
所以我决定使用:afterCompletion
方法HandlerInterceptor
。有两个参数传递给此方法HttpServletRequest
以及HttpServletResponse
其他参数。
Question is: how to get RequestBody
and ResponseBody
from supplied objects?
问题是:如何获取RequestBody
和ResponseBody
获取提供的对象?
As far as I know at Controller we can use @RequestBody
and @ResponseBody
. Can I reuse them at HandlerInterceptor
?
据我所知,我们可以在 Controller 中使用@RequestBody
and @ResponseBody
。我可以重用它们HandlerInterceptor
吗?
采纳答案by simonmarcell
As far as I know, RequestBody
and ResponseBody
can 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.
请求处理完成后的回调,即渲染视图后。将在处理程序执行的任何结果上调用,从而允许适当的资源清理。
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 RequestBody
and ResponseBody
from Interceptor. Here is how I got it working.
这是一个相当古老的线程,但为了那些正在寻找一种方式来获取RequestBody
和ResponseBody
从 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 ResponseBodyAdvice
available 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 install
on 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 RequestBodyAdviceAdapter
and 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 请求输入流转换为您的对象之后。