问题
I have spring boot and I need to log user action in DB, so I wrote HandlerInterceptor:
@Component
public class LogInterceptor implements HandlerInterceptor {
@Autovired
private LogUserActionService logUserActionService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws IOException {
String userName = SecurityContextHolder.getContext().getAuthentication().getName();
String url = request.getRequestURI();
String queryString = request.getQueryString() != null ? request.getQueryString() : "";
String body = "POST".equalsIgnoreCase(request.getMethod()) ? new BufferedReader(new InputStreamReader(request.getInputStream())).lines().collect(Collectors.joining(System.lineSeparator())) : queryString;
logUserActionService.logUserAction(userName, url, body);
return true;
}
}
But according to this answer Get RequestBody and ResponseBody at HandlerInterceptor "RequestBody can be read only once", so as I understand I read input stream and then Spring tries to do same, but stream has been read already and I'm getting an error: "Required request body is missing ..."
So I tried different ways to make buffered input stream i.e.:
HttpServletRequest httpServletRequest = new ContentCachingRequestWrapper(request);
new BufferedReader(new InputStreamReader(httpServletRequest.getInputStream())).lines().collect(Collectors.joining(System.lineSeparator()))
or
InputStream bufferedInputStream = new BufferedInputStream(request.getInputStream());
But nothing helped Also I tried to use
@ControllerAdvice
public class UserActionRequestBodyAdviceAdapter extends RequestBodyAdviceAdapter {
but it has only body, no request info like URL or Request parameters Also tried to use Filters, but result same.
So I need good way to get information from request like user, url, parameters, body(if present) and write it to DB. What is the best way, please help!
回答1:
You can use Filter
to log request body.
public class LoggingFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request);
try {
chain.doFilter(wrappedRequest, res);
} finally {
logRequestBody(wrappedRequest);
}
}
private static void logRequestBody(ContentCachingRequestWrapper request) {
byte[] buf = request.getContentAsByteArray();
if (buf.length > 0) {
try {
String requestBody = new String(buf, 0, buf.length, request.getCharacterEncoding());
System.out.println(requestBody);
} catch (Exception e) {
System.out.println("error in reading request body");
}
}
}
}
The main thing to note here is that you have to pass object of ContentCachingRequestWrapper
in filter chain otherwise you won't get request content in it.
In above example, if you use chain.doFilter(req, res)
or chain.doFilter(request, res)
then you won't get request body in wrappedRequest
object.
回答2:
To log HTTP Request & Response, you can use RequestBodyAdviceAdapter
and ResponseBodyAdvice
. here, it is using in my way.
CustomRequestBodyAdviceAdapter.java
@ControllerAdvice
public class CustomRequestBodyAdviceAdapter extends RequestBodyAdviceAdapter {
@Autowired
HttpServletRequest httpServletRequest;
@Override
public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
return true;
}
@Override
public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,
Class<? extends HttpMessageConverter<?>> converterType) {
// here you can full log httpServletRequest and body.
return super.afterBodyRead(body, inputMessage, parameter, targetType, converterType);
}
}
CustomResponseBodyAdviceAdapter.java
@ControllerAdvice
public class CustomResponseBodyAdviceAdapter implements ResponseBodyAdvice<Object> {
@Autowired
private LoggingService loggingService;
@Override
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
return true;
}
@Override
public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType,
Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
if (serverHttpRequest instanceof ServletServerHttpRequest && serverHttpResponse instanceof ServletServerHttpResponse) {
// here you can full log httpServletRequest and body.
}
return o;
}
}
Above AdviceAdapter
cannot handle the GET
request. So, you can use HandlerInterceptor
.
CustomWebConfigurerAdapter.java
@Component
public class CustomWebConfigurerAdapter implements WebMvcConfigurer {
@Autowired
private CustomLogInterceptor httpServiceInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(httpServiceInterceptor);
}
}
CustomLogInterceptor.java
@Component
public class CustomLogInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
if (DispatcherType.REQUEST.name().equals(request.getDispatcherType().name()) && request.getMethod().equals(HttpMethod.GET.name())) {
// here you can full log httpServletRequest and body for GET Request.
}
return true;
}
}
Here you can reference full source code in my git.
springboot-http-request-response-loging-with-json-logger
+Feature => It is already have Integration with ELK (Elasticsearch, Logstash, Kibana)
来源:https://stackoverflow.com/questions/60556148/how-to-read-request-body-in-handlerinterceptor-in-spring-boot