15

I am using springmvc to create restful api for client, I have an interceptor for checking the accesstoken.

public class AccessTokenInterceptor extends HandlerInterceptorAdapter
{    
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
{
    if (handler instanceof HandlerMethod)
    {
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Authorize authorizeRequired = handlerMethod.getMethodAnnotation(Authorize.class);
        if (authorizeRequired != null)
        {
            String token = request.getHeader("accesstoken");
            ValidateToken(token);
        }
    }
    return true;
}

protected long ValidateToken(String token)
{
    AccessToken accessToken = TokenImpl.GetAccessToken(token);

    if (accessToken != null)
    {
        if (accessToken.getExpirationDate().compareTo(new Date()) > 0)
        {
            throw new TokenExpiredException();
        }
        return accessToken.getUserId();
    }
    else
    {
        throw new InvalidTokenException();
    }
}

And in my controller, I use @ExceptionHandler to handle exceptions, the code to handle InvalidTokenException looks like

@ExceptionHandler(InvalidTokenException.class)
public @ResponseBody
Response handleInvalidTokenException(InvalidTokenException e)
{
    Log.p.debug(e.getMessage());
    Response rs = new Response();
    rs.setErrorCode(ErrorCode.INVALID_TOKEN);
    return rs;
}

But unfortunately the exception throwed in preHandle method is not caught by the exception handler defined in controller.

Can any one give me an solution of handling the exception? PS: My controller method produce both json and xml using code below:

@RequestMapping(value = "login", method = RequestMethod.POST, produces =
{
    "application/xml", "application/json"
})
4
  • 3
    You cannot. The @ExceptionHandler methods only apply to that specific controller. The interceptor executes before the controller so there is no way to know, at that point, which @ExceptionHandler methods would apply. Not sure if a @ControllerAdvice bean would help here, to register global error handling methods, else implement your own HandlerExceptionResolver to implement it in a generic way instead of in your controllers. Commented Feb 27, 2014 at 8:01
  • what type of exception throwing preHandle method? Commented Feb 27, 2014 at 9:59
  • The ValidateToken method should be lowercase, like any other method. I am facging the same issue but it seems indeed that the Controller method is never reachted when throwing an exception from the preHandle method so an @ControllerAdvice will not catch up and handle this exception. Commented Nov 17, 2016 at 8:53
  • 2
    I am seeing same behaviour as @klausch. ControllerAdvice methods not being executed when exception thrown in preHandle Commented Dec 5, 2019 at 3:52

5 Answers 5

6

Solved using other approach, catch exception and forward to another controller.

try
{
    ValidateToken(token);
} catch (InvalidTokenException ex)
{
    request.getRequestDispatcher("/api/error/invalidtoken").forward(request, response);
    return false;
} catch (TokenExpiredException ex)
{
    request.getRequestDispatcher("/api/error/tokenexpired").forward(request, response);
    return false;
}
Sign up to request clarification or add additional context in comments.

2 Comments

No need of throwing exception. Throwing exception leads to performance issue. Return a value based on condition and set response.setStatus() or modify response object as required. Finally 'return false' will ensure response is sent back to the client.
I had to do similar implementation, and redirecting to the error page solved my case.
6

Moving your @ExceptionHandler methods into a @ControllerAdvice annotated class can help here. See: ControllerAdvice

Rembo suggested it in comment already (marked as "not sure"), I confirm that works for me: in this case the thrown exceptions are caught correctly.

3 Comments

I am using a ControllerAdvice class, and it is not working for me. My @ExceptionHandler handle code is not being executed when exception is thrown by the preHandle method. Springboot 2.0.2
@Sodved - found solution ?, i'm having same problem
@Rajat We just manually worked around it. In our preHandle methods we have try/catch and in the catch we explicitly create an instance of our controllerAdvice class, use its methods to generate correct status and message, then explicitly write those to our serveletResponse object. Not that clean but works
1

Add a default Controller such as FallbackController with an empty RequestMapping path method to handle all Exception requests:

@Controller
public class FallbackController {
    @RequestMapping(method = {RequestMethod.GET, RequestMethod.POST})
    @ResponseBody
    public String fallback(HttpServletRequest request, HttpServletResponse response) {
        return "Anything you want";
    }

    @ExceptionHandler(InvalidTokenException.class)
    public @ResponseBody Response handleInvalidTokenException(InvalidTokenException e) {
        Log.p.debug(e.getMessage());
        Response rs = new Response();
        rs.setErrorCode(ErrorCode.INVALID_TOKEN);
        return rs;
    }
}

Hope it helps.

Comments

0

You have invalid return type, that's why not caught by the exception handler

The following return types are supported for handler methods:

  • A ModelAndView object (Servlet MVC or Portlet MVC).
  • A Model object, with the view name implicitly determined through a RequestToViewNameTranslator.
  • A Map object for exposing a model, with the view name implicitly determined through a RequestToViewNameTranslator.
  • A View object.
  • A String value which is interpreted as view name.
  • void if the method handles the response itself (by writing the response content directly, declaring an argument of type ServletResponse / HttpServletResponse / RenderResponse for that purpose) or if the view name is supposed to be implicitly determined through a RequestToViewNameTranslator (not declaring a response argument in the handler method signature; only applicable in a Servlet environment).

Try by changing you return type, to get it work.

Refference: spring source

Comments

0

If you use @EnableWebMvc annotation anywhere through ur application, HandlerExceptionResolverComposite (subclass of HandlerExceptionResolver) will be applied. Since we know that HandlerExceptionResolver will be invoked not only through the controller method execution cycle but also before/after controller (e.g. HandlerInterceptor. check here), HandlerExceptionResolverComposite will be invoked. Since by default, HandlerExceptionResolverComposite will register 3 resolvers, and one of them is: ExceptionHandlerExceptionResolver, based on https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolver.html#doResolveHandlerMethodException-javax.servlet.http.HttpServletRequest-javax.servlet.http.HttpServletResponse-org.springframework.web.method.HandlerMethod-java.lang.Exception-

it will try to find controller level @ExceptionHandler annotation and forward the exception to that exception handler. (see "doResolveHandlerMethodException" in above link)

So as long as you have @EnableWebMvc (why not?), your @ExceptionHandler should be able to catch exception thrown from spring interceptor.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.