Reputation: 151
I have written a request/response logger using @Around
:
@Around(value = "execution(* com.xyz.example.controller.*.*(..))")
public Object logControllers(ProceedingJoinPoint joinPoint) throws Throwable {
Object response = joinPoint.proceed();
// Log request and response
return response;
}
However, I realized if the request input (ie. request body) provided are invalid, for example, if number
is a required field in the request body, and it must be an Integer
, but I entered a String
as its value and send to the endpoint, Spring will return a 400 response without hitting this aspect. But if I entered some legit input, let the request actually goes through the endpoint, then this aspect will do its job. So is there any way to let this aspect works under the situation I mentioned above?
PS: I have tried using @ControllerAdvice
with @ExceptionHandler
, but looks like it will also not go through the above logger aspect if @ControllerAdvice
is triggered.
Upvotes: 1
Views: 2855
Reputation: 42441
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):
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?
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:
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:
Upvotes: 4