How Spring MVC prints @RequestBody @Response logs

  • 2021-06-28 12:44:28
  • OfStack

Description of the problem:

When using JSON to receive front-end parameters, the SpringMVC default output log is as follows:

o.s.web.servlet.DispatcherServlet : POST "/example_project/app/login", parameters={}
parameters={} could not print the contents of the JSON message.

If you print parameters yourself, you need to get the JSON content from reqeust.getInputStream, but since the stream can only be read once, subsequent SpringMVC parsing parameter exceptions will result.

A comparative solution was found on the web: re-encapsulate Reqeust with HttpRequestWrapper to enable SpringMVC to parse HttpReqeust normally after printing the log.This method is cumbersome and will not be studied here

The main point here is to say that Spring offers a better solution:

Log output can be achieved by customizing RequestBodyAdvisor and ResponseBodyAdvisor.

RequestBodyAdvisor can get the parsed Controller method parameter object. ResponseBodyAdvisor can get the Controller method return value object.

Then register them with requestMappingHandlerAdapter:


//  inherit WebMvcConfigurationSupport ,   Override the method 
@Override
@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
  RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter();
  adapter.setRequestBodyAdvice(Lists.newArrayList(new CustomerRequestBodyAdvisor()));
  adapter.setResponseBodyAdvice(Lists.newArrayList(new CustomerResponseBodyAdvisor()));
  return adapter;
}

CustomerRequestBodyAdvisor, CustomerResponseBodyAdvisor log output implementation reference is also attached:

RequestBodyAdvisor Implementation Reference:


// CustomerRequestBodyAdvisor.java
/**
*  Print request parameter log 
*/
public class CustomerRequestBodyAdvisor extends RequestBodyAdviceAdapter {

  private static final Logger logger = LoggerFactory.getLogger(CustomerRequestBodyAdvisor.class);

  @Override
  public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
    //  Processing only @RequestBody Annotated parameters 
    return methodParameter.getParameterAnnotation(RequestBody.class) != null;
  }

  @Override
  public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
    Method method = parameter.getMethod();
    
    //  Parameter object to object JSON Character string 
    String jsonBody;
    if (StringHttpMessageConverter.class.isAssignableFrom(converterType)) {
      jsonBody = body.toString();
    } else {
      jsonBody = JSON.toJSONString(body, SerializerFeature.UseSingleQuotes);
    }
    
    //  Custom Log Output 
    if (logger.isInfoEnabled()) {
      logger.info("{}#{}: {}", parameter.getContainingClass().getSimpleName(), method.getName(), jsonBody);
      //      logger.info("json request<=========method:{}#{}", parameter.getContainingClass().getSimpleName(), method.getName());
    }
    return super.afterBodyRead(body, inputMessage, parameter, targetType, converterType);
  }
}

ResponseBodyAdvisor Implementation Reference:


// CustomerResponseBodyAdvisor.java
/**
*  Print response value log 
*/
public class CustomerResponseBodyAdvisor implements ResponseBodyAdvice<Object> {
  private static final Logger logger = LoggerFactory.getLogger(CustomerResponseBodyAdvisor.class);

  @Override
  public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
    return AbstractJackson2HttpMessageConverter.class.isAssignableFrom(converterType)
        || returnType.getMethod().isAnnotationPresent(ResponseBody.class);
  }

  @Override
  public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
    //  Response Value Rotation JSON String Output to Log System 
    if (logger.isInfoEnabled()) {
      logger.info("{}: {}", request.getURI(), JSON.toJSONString(body, SerializerFeature.UseSingleQuotes));
    }
    return body;
  }

}

Related articles: