Announcement Announcement Module
Collapse
No announcement yet.
AOP around @Controller method Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • AOP around @Controller method

    I have added an aspect to my app that advises my Controller beans:

    Code:
    @Aspect
    public class ResponseAspect {
    	@Around("execution(* com.myapp.controller.*.*(..)) && @target(org.springframework.stereotype.Controller) && @annotation(org.springframework.web.bind.annotation.RequestMapping)")
        public Object wrapResponse(ProceedingJoinPoint call) throws Throwable {
    		try {
    			Object result = call.proceed();
    			return new Response<Object>("ok", result);
    
    		} catch (InvalidParameterException ex) {
    			return new ErrorResponse(ex.getValidationMessage(), ex.getFailures());
    
    		} catch (DuplicatedKeyException ex) {
    			return new ErrorResponse(ex.getMessage(), null);
    		}
    This aspect wraps any Object returned by Controllers into a Response. The aspect works correctly, and when I make an HTTP request, I get a Response object in the HTTP response instead of the object originally returned by the Controller.

    Also, when the Controller method throws an Exception, the aspect correctly catches it, and the HTTP response correctly shows me the ErrorResponse object.

    My problem is, I had exception handler methods in my app. They catch exceptions and return a different HTTP status code according to the Exception. For instance, I used to return 400 Bad Request for InvalidArgumentException and 404 Not Found for EmptyResultDataAccessException.


    Code:
        @ResponseStatus(HttpStatus.BAD_REQUEST)
        @ExceptionHandler(InvalidParameterException.class)
        public @ResponseBody ErrorResponse validationException(InvalidParameterException ex) {
            return new ErrorResponse(ex.getValidationMessage(), ex.getFailures());
        }
    
        @ResponseStatus(HttpStatus.NOT_FOUND)
        @ExceptionHandler(EmptyResultDataAccessException.class)
        public @ResponseBody ErrorResponse notFoundException(EmptyResultDataAccessException ex) {
            return new ErrorResponse(ex.getMessage(), null);
        }
    Somehow, the aspect is messing with the Exception Handlers. When I added the aspect, the Exception Handlers are no longer invoked. The ErrorResponse objects are returned by the aspect, but I still get the HTTP status code 200 OK, even when there was an exception in the Controller.

    What can I do to change the HTTP status code of the response and keep the aspect advising the Controller methods ?

  • #2
    The ExceptionHandlers are invoked when an exception occurs. In other words when the DispatcherServlet catches an exception, now what are you doing in your aspect, catching a exception basically making it unavailable (it is already handled and everything seems ok for the dispatcherservlet) for the servlet. In short don't catch the exception if you have an ExceptionHandler for it.

    Comment


    • #3
      Solved.

      Marten was right, the aspect should not catch the exceptions; they should float up the stack.

      The correct code looks like this:

      Code:
      @Aspect
      public class ResponseAspect {
      	@Around("execution(* br.com.elo.backend.controller.*.*(..)) && @target(org.springframework.stereotype.Controller) && @annotation(org.springframework.web.bind.annotation.RequestMapping)")
          public Object wrapResponse(ProceedingJoinPoint call) throws Throwable {
      		Object result = call.proceed();
      		return new Response<Object>("ok", result);
          }
      }
      Also, if anyone uses this solution, make sure to tweak the pointcut so that it will only advise methods marked with @RequestMapping. Otherwise, the exception handler methods would also be advised, and this may cause problems.

      Code:
      @Around("execution(* br.com.elo.backend.controller.*.*(..)) && @target(org.springframework.stereotype.Controller) && @annotation(org.springframework.web.bind.annotation.RequestMapping)")

      Comment

      Working...
      X