Java 如何在 Spring Interceptor 中使用@ExceptionHandler?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22062311/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 13:19:59  来源:igfitidea点击:

How to use @ExceptionHandler in Spring Interceptor?

javaspringexception-handlinginterceptor

提问by Wp7Beginner

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

我正在使用 springmvc 为客户端创建restful api,我有一个用于检查访问令牌的拦截器。

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 的代码看起来像

@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.

但不幸的是,在 preHandle 方法中抛出的异常没有被控制器中定义的异常处理程序捕获。

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

谁能给我一个处理异常的解决方案?PS:我的控制器方法使用以下代码生成json 和 xml

@RequestMapping(value = "login", method = RequestMethod.POST, produces =
{
    "application/xml", "application/json"
})

回答by Abhishek Nayak

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

您的返回类型无效,这就是异常处理程序未捕获的原因

The following return types are supported for handler methods:

处理程序方法支持以下返回类型:

  • A ModelAndViewobject (Servlet MVC or Portlet MVC).
  • A Modelobject, with the view name implicitly determined through a RequestToViewNameTranslator.
  • A Mapobject for exposing a model, with the view name implicitly determined through a RequestToViewNameTranslator.
  • A Viewobject.
  • A Stringvalue which is interpreted as view name.
  • voidif the method handles the response itself (by writing the response content directly, declaring an argument of type ServletResponse/ HttpServletResponse/ RenderResponsefor 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).
  • 一个ModelAndView对象(Servlet MVC 或 Portlet MVC)。
  • 一个Model对象,其视图名称通过RequestToViewNameTranslator.
  • Map用于曝光模式,与视图名称对象隐含地通过确定RequestToViewNameTranslator
  • 一个View对象。
  • 一个String被解释为视图名称的值。
  • void如果该方法处理反应本身(通过直接写入的响应内容,声明类型的参数ServletResponse/ HttpServletResponse/RenderResponse为此目的),或者如果视图名称应该通过一个被隐式地确定RequestToViewNameTranslator(未声明的处理程序方法签名的响应参数; 仅适用于 Servlet 环境)。

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

尝试改变你的返回类型,让它工作。

Refference: spring source

参考:弹簧源

回答by Wp7Beginner

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

回答by istibekesi

Moving your @ExceptionHandlermethods into a @ControllerAdviceannotated class can help here. See: ControllerAdvice

将你的@ExceptionHandler方法移动到一个带@ControllerAdvice注释的类中可以在这里有所帮助。请参阅:控制器建议

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

Rembo已经在评论中建议它(标记为“不确定”),我确认这对我有用:在这种情况下,抛出的异常被正确捕获。

回答by user3386184

If you use @EnableWebMvc annotation anywhere through ur application, HandlerExceptionResolverComposite (subclass of HandlerExceptionResolver)will be applied. Since we know that HandlerExceptionResolverwill be invoked not only through the controller method execution cycle but alsobefore/after controller (e.g. HandlerInterceptor. check here), HandlerExceptionResolverCompositewill 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-

如果您通过您的应用程序在任何地方使用 @EnableWebMvc 注释,HandlerExceptionResolverComposite (subclass of HandlerExceptionResolver)将被应用。因为我们知道,HandlerExceptionResolver不仅将通过控制器方法执行周期被调用,而且前/控制器(例如HandlerInterceptor接口。检查后在这里),HandlerExceptionResolverComposite将被调用。由于默认情况下,HandlerExceptionResolverComposite 将注册 3 个解析器,其中之一是:ExceptionHandlerExceptionResolver基于 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)

它将尝试找到控制器级别的 @ExceptionHandler 注释并将异常转发到该异常处理程序。(请参阅上面链接中的“doResolveHandlerMethodException”)

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

所以只要你有@EnableWebMvc(为什么不呢?),你的@ExceptionHandler 就应该能够捕获从 spring 拦截器抛出的异常。

回答by Lion

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

添加一个默认的Controller,比如FallbackController,带有一个空的RequestMapping路径方法来处理所有的异常请求:

@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.

希望能帮助到你。