How to read request body in HandlerInterceptor in Spring boot?

社会主义新天地 提交于 2021-01-29 10:04:16

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!