【问题标题】:How to use @ExceptionHandler in Spring Interceptor?Spring拦截器中如何使用@ExceptionHandler?
【发布时间】:2014-03-30 12:23:14
【问题描述】:

我正在使用springmvc为客户端创建restful api,我有一个用于检查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();
    }
}

在我的控制器中,我使用@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;
}

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

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

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

【问题讨论】:

  • 你不能。 @ExceptionHandler 方法仅适用于该特定控制器。拦截器在控制器之前执行,因此此时无法知道哪些@ExceptionHandler 方法将适用。不确定@ControllerAdvice bean 是否会在这里帮助注册全局错误处理方法,否则实现您自己的HandlerExceptionResolver 以通用方式而不是在您的控制器中实现它。
  • preHandle 方法抛出什么类型的异常?
  • ValidateToken 方法应该是小写的,就像任何其他方法一样。我面临同样的问题,但似乎确实在从 preHandle 方法抛出异常时永远不会到达 Controller 方法,因此 @ControllerAdvice 不会赶上并处理此异常。
  • 我看到的行为与@klausch 相同。在 preHandle 中抛出异常时未执行 ControllerAdvice 方法

标签: java spring exception-handling interceptor


【解决方案1】:

你有无效的返回类型,这就是为什么没有被异常处理程序捕获

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

  • ModelAndView 对象(Servlet MVC 或 Portlet MVC)。
  • 一个Model 对象,其视图名称通过RequestToViewNameTranslator 隐式确定。
  • 用于公开模型的Map 对象,视图名称通过RequestToViewNameTranslator 隐式确定。
  • View 对象。
  • String 值,被解释为视图名称。
  • void 如果方法本身处理响应(通过直接编写响应内容,为此目的声明 ServletResponse / HttpServletResponse / RenderResponse 类型的参数)或者视图名称是否应该通过隐式确定RequestToViewNameTranslator(未在处理程序方法签名中声明响应参数;仅适用于 Servlet 环境)。

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

参考:spring source

【讨论】:

    【解决方案2】:

    使用其他方法解决,捕获异常并转发到另一个控制器。

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

    【讨论】:

    • 不需要抛出异常。抛出异常会导致性能问题。根据条件返回值并设置 response.setStatus() 或根据需要修改响应对象。最后,'return false' 将确保将响应发送回客户端。
    • 我不得不做类似的实现,重定向到错误页面解决了我的情况。
    【解决方案3】:

    将您的 @ExceptionHandler 方法移动到 @ControllerAdvice 带注释的类中会有所帮助。见:ControllerAdvice

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

    【讨论】:

    • 我正在使用 ControllerAdvice 类,但它对我不起作用。当preHandle 方法抛出异常时,我的@ExceptionHandler handle 代码没有被执行。 Springboot 2.0.2
    • @Sodved - 找到解决方案?,我遇到了同样的问题
    • @Rajat 我们只是手动解决了它。在我们的 preHandle 方法中,我们有 try/catch,在 catch 中,我们显式地创建了 controllerAdvice 类的实例,使用它的方法来生成正确的状态和消息,然后显式地将它们写入我们的 serveletResponse 对象。不是很干净,但很有效
    【解决方案4】:

    如果您通过您的应用程序在任何地方使用@EnableWebMvc 注释,则将应用HandlerExceptionResolverComposite (subclass of HandlerExceptionResolver)。因为我们知道HandlerExceptionResolver不仅会通过控制器方法执行周期而且在控制器之前/之后被调用(例如HandlerInterceptor.checkhere),所以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-

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

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

    【讨论】:

      【解决方案5】:

      添加一个默认的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;
          }
      }
      

      希望对你有帮助。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-08-09
        • 1970-01-01
        • 1970-01-01
        • 2014-10-06
        • 2014-05-19
        • 2017-02-25
        • 1970-01-01
        • 2021-12-18
        相关资源
        最近更新 更多