springboot mvc log request body string(in Filter) before RestController

125
January 27, 2022, at 10:20 AM

I know basics of springboot mvc but don't well understand how the underlying classes work(such as Servlet, Intercepters, Filters). Searched but didn't find answer.

To illustrate the problem, I created a demo project in github.

  • the controller:
@RestController
public class GreetController {
    protected static final Logger log = LogManager.getLogger();
    @PostMapping("/")
    public String greet(@RequestBody final WelcomeMessage msg) {
        log.info(msg);
        return "Hello " + msg.from;
    }
    @ExceptionHandler({HttpMessageNotReadableException.class})
    public String addStudent(HttpMessageNotReadableException e) {
        log.error(e.getMessage());
        return "greeting from @ExceptionHandler";
    }
}
  • the client
    1. valid request curl -H "Content-Type: application/json" http://localhost:8080 --data '{"from":"jim","message":"nice to meet you!"}'

    2. invalid request(invalid json) curl -H "Content-Type: application/json" http://localhost:8080 --data '{"from":"jim","message""nice to meet you!"}'

  • the ultimate goal:

log request body string before RestController's methods enter.

'log' here just represents some other logic that must be handled before RestController(the two methods). For example, I want to record the request body string to ThreadLocal, then in @ExceptionHandler I'll get that string and log as ERROR.

I once tried HandlerInterceptor but will get some error like

'java.lang.IllegalStateException: Cannot call getInputStream() after getReader() has already been called for the current request'.

after some searching 1 2, I decided to use Filter with ContentCachingRequestWrapper.

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        ContentCachingRequestWrapper cachedRequest = new ContentCachingRequestWrapper(httpServletRequest);
        chain.doFilter(cachedRequest, response);
        String requestBody = IOUtils.toString(cachedRequest.getContentAsByteArray(), cachedRequest.getCharacterEncoding());
        log.info(requestBody);
    }

This code works well except that the log is after the RestController. if I change the order:

        String requestBody = IOUtils.toString(cachedRequest.getReader());
        log.info(requestBody);
        chain.doFilter(cachedRequest, response);

Works for invalid request, but when request is valid, got following exception:

com.example.demo.GreetController : Required request body is missing: public java.lang.String com.example.demo.GreetController.greet(com.example.demo.WelcomeMessage)

I also tried getContentAsByteArray, getInputStream and getReader methods since some tutorials say the framework checks for specific method call. but all in vain.

Now I'm bit confused. Can anyone explain the executing order of RestController and Filter, when request is valid and invalid? Is there any easier way(less code) to achive my ultimate goal? thanks!

I'm using springboot 2.6.3, jdk11.

Rent Charter Buses Company
READ ALSO
How can I capture the error thrown by a python script in the bash script calling it?

How can I capture the error thrown by a python script in the bash script calling it?

I want to write a bash script that sends a message depending on whether a python script it called ran successfully or threw an error

108
Can I get original page be different than the AMP?

Can I get original page be different than the AMP?

So I am using IFRAME on my pagesThe code usually goes like this

79
Where PWA store photos?

Where PWA store photos?

I've the PWA app, launched on Android deviceThe app allow to take pictures and upload them to server

159
CSS Progress Circle

CSS Progress Circle

I have searched this website to find progress bars, but the ones I have been able to found show animated circles that go to the full 100%

153