【问题标题】:handling wrapped exceptions in spring mvc处理spring mvc中的包装异常
【发布时间】:2013-03-09 21:24:11
【问题描述】:

我有 Spring MVC 和 jackson。当我开始一个不正确的请求时,Jackson 映射失败并抛出UnrecognizedPropertyException。我想使用

来处理这个异常
@ExceptionHandler
public String handle(UnrecognizedPropertyException e) {
  ...
}

但是 Spring 将此异常包装在 HttpMessageConversionException 中,因此上面的代码不起作用。在 Spring 中是否可以处理 Jackson 特定(或一般库特定)异常?

【问题讨论】:

    标签: java spring exception spring-mvc exception-handling


    【解决方案1】:

    很遗憾,UnrecognizedPropertyException 是IOException 的子类型。处理@RequestBody 的RequestResponseBodyMethodProcessor(我假设这是发生异常的地方)对IOException(解释为请求输入流的失败)进行了特殊处理,将其包装在HttpMessageNotReadableException 中。另外,HttpMessageConverter接口被指定在read期间出现转换错误时抛出HttpMessageNotReadableException。

    无论如何,你都必须处理这个问题(如果 Jackson 抛出未经检查的异常,情况可能会有所不同)。

    幸运的是,since 4.3, Spring MVC's ExceptionHandlerMethodResolver(处理@ExceptionHandler)可以解开cause 的异常(see SPR-14291)。因此,假设您在HttpMessageNotReadableException 的继承层次结构中没有任何异常处理程序,您的处理程序方法

    @ExceptionHandler
    public String handle(UnrecognizedPropertyException e) {
        ...
    }
    

    将用于处理异常。这发生在 Spring MVC 查找可以处理 HttpMessageNotReadableException 的处理程序方法之后,然后使用 Throwable#getCause 解包嵌套异常并再次尝试查找。


    在 4.3 之前的版本中,或者如果您在 HttpMessageNotReadableException 的继承层次结构中确实有异常类型的处理程序,您始终可以在自己提取原因后进行委托。

    @ExceptionHandler
    public String handle(HttpMessageConversionException e) throws Throwable {
        Throwable cause = e.getCause();
        if (cause instanceof UnrecognizedPropertyException) {
            handle((UnrecognizedPropertyException) cause);
        }
        ...
    }
    
    public String handle(UnrecognizedPropertyException e) {
        ...
    }
    

    【讨论】:

    • @piotrek 你确定这行得通吗?我们在 Spring 4.2 中已经尝试过了,但没有。 Spring 只是记录一条调试消息并丢弃异常(ExceptionHandlerExceptionResolver 中的logger.debug("Failed to invoke @ExceptionHandler method: " + exceptionHandlerMethod, invocationEx))
    • @DidierL 你是对的。这是很久以前的事了,我不记得我发现了什么。为 4.3 编辑。
    • @SotiriosDelimanolis 现在这是编辑之一!我在发帖my own question 时没想到会这样。由于我们也是 4.3 之前的版本,您认为我的解决方案如何避免您提出的解决方案所需的所有 instanceof?
    • @DidierL 更好的是,except 当您可能有多个 HandlerExceptionResolver bean 时。您可以考虑注入 HandlerExceptionResolverComposite 并让它自己解决。
    • @DidierL 是的,如果您使用WebMvcConfigurationSupport 或<mvc:annotation-driven />,它将公开一个包含一些默认实现的HandlerExceptionResolverComposite bean(除非您已声明自己的bean)。
    【解决方案2】:

    我是这样做的:

    /**
     * Global exception handler for unhandled errors.
     * @author Varun Achar
     * @since 2.0
     * @version 1.0
     *
     */
    public class Http500ExceptionResolver extends SimpleMappingExceptionResolver
    {
        @Inject
        private ViewResolver resolver;
    
        @Override
        public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
        {
            ModelAndView mv = new ModelAndView();
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            if(CommonUtil.isAjax(request))
            {
                MappingJackson2JsonView view = new MappingJackson2JsonView();
                view.setObjectMapper(JsonUtil.getObjectMapper());
                mv.addObject("responseMessage", "We had some problems while serving your request. We are looking into it");
                mv.addObject("responseCode", GenericResponse.ERROR.code());
                mv.addObject("success", false);
                mv.setView(view);
            }
            else
            {
                mv.setViewName(resolver.getView(ViewConstants.ERROR_PAGE));
            }
            return mv;
        }
    }
    

    在我的 servlet-context 中:

       <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver">
            <property name="order" value="0" />
        </bean>
        <bean id="securityExceptionResolver"
            class="com.trelta.commons.utils.security.SecurityExceptionResolver">
            <property name="order" value="1"></property>
            <property name="exceptionMappings">
                <map>
                    <entry key="org.springframework.security.access.AccessDeniedException"
                        value="/common/accessDenied"></entry>
                    <entry key="org.springframework.security.core.AuthenticationException"
                        value="/common/authenticationFailure"></entry>
                </map>
            </property>
        </bean>
        <bean id="http500ExceptionResolver"
                class="com.trelta.commons.utils.security.Http500ExceptionResolver">
                <property name="order" value="3" />
        </bean>
    

    order 字段很重要,因为 Spring 按该顺序循环通过异常解析器。您还可以为自己定义此类异常映射,一切顺利!

    查看this 博客文章和SimpleMappingExceptionResolver 的javadoc

    【讨论】:

    • 我认为我们有误会。问题是我不能做&lt;entry key=""...UnrecognizedPropertyException",因为春天结束了这个异常。这不是我的代码引发的异常。它是杰克逊在参数绑定期间抛出的
    • 哦.. 明白了.. Spring 结束了异常。我检查了spring的源代码。你不能这样做,除非你重写类方法。 @Sotirios 的方法似乎最好
    【解决方案3】:

    我们正在使用 org.apache.commons.lang.exception.ExceptionUtils ...

    private myMethod (Throwable t) {
    
        if (ExceptionUtils.getRootCause(t) instanceof MyException) ...
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-04-10
      • 1970-01-01
      • 2016-12-30
      • 2011-10-08
      • 1970-01-01
      相关资源
      最近更新 更多