【问题标题】:@ExceptionHandler + @ResponseStatus@ExceptionHandler + @ResponseStatus
【发布时间】:2013-12-05 09:00:19
【问题描述】:

我正在使用以下代码处理我的受控异常:

  @ExceptionHandler(MyException.class)
       @ResponseStatus(HttpStatus.NOT_FOUND)
       public ModelAndView handleMyException(MyException e) {
          ModelAndView mav = new ModelAndView(ERROR_PAGE);
          (...)
          return mav;
       }

也就是说,我想对不同的错误使用自定义视图,并对 HTTP 响应使用响应状态代码。

同时,对于纯 404 我在 web.xml 中有以下配置

<error-page>
        <error-code>404</error-code>
        <location>/404</location>
    </error-page>

    <error-page>
        <error-code>400</error-code>
        <location>/400</location>
    </error-page>

采用 404 特定视图。

问题是当NOT_FOUND从我的@ExceptionHandled方法抛出时,它没有显示我的自定义视图,调试显示执行实际上是通过handleMyException方法,但是在它完成之后它也通过在 web.xml 中映射/404 的方法,即显示的视图。

此外,如果我抛出不同的响应代码,我会在异常上获得默认行为,而不是我的自定义视图。

【问题讨论】:

  • 你的问题我不清楚。 1) ERROR_PAGE 的值是多少? 2)什么是实际行为,什么是想要的行为? 3)请澄清“NOT_FOUND is throw from my @ExceptionHandled method”是指“@ExceptionHandled method set response code to NOT_FOUND”
  • 你有什么不明白的?
  • 总结一下:我想在抛出某个异常时显示我的自定义错误视图,并且仍然返回404的响应代码。问题是,通过定义实际404发生时的默认操作,它会覆盖我在 @ExceptionHandler 方法上设置的视图。
  • 现在清除。但答案是未知的......
  • 你的Spring版本和应用服务器是什么?我检查了 Spring 3.2.5.RELEASE 和 Jetty 9.0.6,但无法重现您的情况。而且我认为根据 Servlet 3.0 规范(使用给定的 Spring 版本)它不应该是可重现的

标签: spring http jakarta-ee spring-mvc


【解决方案1】:

我无法在 Tomcat 6 和 Spring 2.3.4 中重现您的问题。这是正确的,因为根据 Servlet 规范 2.5,部署描述符定义了一个错误列表 页面说明。该语法允许返回资源的配置 当 servlet 或过滤器调用 sendError 时由容器 关于特定状态代码的响应 (...)

我跟踪了 Spring 根据 @ResponseStatus(HttpStatus.NOT_FOUND) 设置响应代码的位置 在这里:

public class ServletInvocableHandlerMethod (...)     
private void setResponseStatus(ServletWebRequest webRequest) throws IOException {
        if (this.responseStatus == null) {
            return;
        }

        if (StringUtils.hasText(this.responseReason)) {
            webRequest.getResponse().sendError(this.responseStatus.value(), this.responseReason);
        }
        else {
            webRequest.getResponse().setStatus(this.responseStatus.value());
        }

        // to be picked up by the RedirectView
        webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, this.responseStatus);
    }

在我的情况下,如果错误处理方法被注释

 @ResponseStatus(HttpStatus.NOT_FOUND)

选择了以下分支:

else {
        webRequest.getResponse().setStatus(this.responseStatus.value());
    }

因为调用了 HttpServletResponse.setStatus 而不是 HttpServletResponse.sendError,所以 Web 容器会忽略 &lt;error-code&gt;404&lt;/error-code&gt; 中定义的错误页面

我希望我的解释对您自己跟踪问题有用。我怀疑在某处调用了 HttpServletResponse.sendError 并触发 Web 容器返回默认错误页面

【讨论】:

  • 感谢您的透彻分析。现在我想我可以看到问题出在哪里:我实际上是在用一个原因注释我的方法(使用@ResponseStatus(value=HttpStatus.NOT_FOUND, reason="My custom reason"),所以从你发布的代码来看,这使得 Spring 通过备用分支。不幸的是,我确实需要定义我自己的状态消息,所以这对我来说有点可惜。我想我必须寻找不同的方法。
  • @kelmer 为什么不将您的状态消息(原因)作为自定义视图的一部分? Also related.
【解决方案2】:

听起来问题可能是 Web 容器正在尝试处理从 Web 应用程序看到的 400/404(因为它不了解这些错误的上下文)。您可能需要摆脱 web.xml 错误页面定义并向 Spring 控制器添加更多配置以处理通用 400/404 错误。

当我在我的应用程序中设置异常处理时,本指南帮助了我很多:http://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc

web.xml 告诉应用容器如何处理应用生成的各种响应代码。当您从控制器方法中获取异常时,它将由 Spring @ExceptionHandler 注释方法处理。此时,应用容器尚未参与,因此它还不知道发生了什么。

我最好的理解是,当您从异常处理程序方法生成 404 Http 状态并返回时,Spring 基本上到那时就完成了,应用程序容器返回并说“我得到了 404,我该怎么办404?啊,重定向到 /404”。然后,控制权返回到 Web 应用程序本身来处理 /404 请求。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-22
    • 1970-01-01
    • 2012-07-29
    相关资源
    最近更新 更多