First of all, let me explain why your attempts with aspect and controller advice have failed.
The flow goes like this (greatly simplified but still hopefully 'deep' enough to explain the point):
- The Request hits the web contains (tomcat for instance)
- Tomcat converts the HTTP raw request text (payload) into java classes that represent the http request (HttpServletRequest etc)
- If its specified the request passes through javax.servlet.Filter-s which operate on "raw" request objects.
- Tomcat finds the servlet to handle the request. In this case its Spring MVC's DispatcherServlet
- Tomcat passes the execution to DispatcherServlet. At this point spring comes into picture.
- DispatcherServlet finds the controller to be called
- DispatcherServlet "understands" how to convert the data from the raw request to the parameters of the method specified in controller (what should be mapped to "path", how to convert Body and so forth). For example if its expected to be JSON, Jackson will be used for actual conversion.
- The controller is invoked
Now, regrading the AOP part:
AOP advice wraps your controller effectively providing a proxy "indistinguishable" from the real controller. So this proxy could be invoked (transparently to Spring MVC) during the step "8"
This is a "happy path". However, what happens if the query is wrong?
- If its not an Http Request it will fail during the step 2 and clearly won't reach step 8
- If its a valid http request but is not mapped correctly (like the wrong url mapping path, etc) DispatcherServlet won't find a relevant controller to handle it, so it will fail during step "6"
- If the JSON payload is wrong, step 7 will fail.
In any case it won't reach step 8, and thats the reason of why your advice is not invoked
Now regarding the @ControllerAdvice
. Its a "special" exception handling mechanism in spring MVC that helps to properly map exceptions that happen inside the controller (or the class that controller calls to, like service, Dao, etc.). Since the flow haven't even reached the controller, its pretty much irrelevant here.
Now in terms or resolution:
There are basically two abstractions that you can try to do it programmatically:
- javax.servlet's Filter to do it in a web container's way
- HandlerInterceptorAdapter to do it in a spring MVC's way
In both cases you'll have to deal with the raw request.
Here is an example of the spring mvc way:
@Component
public class LoggingInterceptor
extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler) {
return true; // called before the actual controller is invoked
}
@Override
public void afterCompletion(
HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
// called after the controller has processed the request,
// so you might log the request here
}
}
In order to register and map the interceptor like this correctly you can use WebMvcConfigurer
. All-in-all see this example
Other solutions include:
- Spring Boot Actuator has an endpoint that logs 50 last requests
- Tomcat (if you use tomcat for example) has a special Valve that can log request (access log style). See this thread for instance