简体   繁体   中英

How to use @ExceptionHandler in Spring Interceptor?

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"
})

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.

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;
}

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.

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

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 (eg 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM