【问题标题】:Spring MVC: How to return custom 404 errorpages?Spring MVC:如何返回自定义 404 错误页面?
【发布时间】:2014-01-30 10:46:02
【问题描述】:

当找不到请求的资源时,我正在寻找一种干净的方法来在 Spring 4 中返回自定义的 404 错误页面。查询不同的域类型会导致不同的错误页面。

这里有一些代码来表明我的意图(Meter 是一个域类):

@RequestMapping(value = "/{number}", method = RequestMethod.GET)
public String getMeterDetails(@PathVariable("number") final Long number, final Model model) {
    final Meter result = meterService.findOne(number);
    if (result == null) {
        // here some code to return an errorpage
    }

    model.addAttribute("meter", result);
    return "meters/details";
}

我想出了几种处理问题的方法。首先有可能创建RuntimeExceptions 之类的

@ResponseStatus(HttpStatus.NOT_FOUND)
public class MeterNotFoundExcption extends RuntimeException { }

然后使用异常处理程序呈现自定义错误页面(可能包含指向仪表列表的链接或任何适当的内容)。

但我不喜欢用许多小例外来污染我的应用程序。

另一种可能性是使用HttpServletResponse 并手动设置状态码:

@RequestMapping(value = "/{number}", method = RequestMethod.GET)
public String getMeterDetails(@PathVariable("number") final Long number, final Model model,
final HttpServletResponse response) {
    final Meter meter = meterService.findOne(number);
    if (meter == null) {
        response.setStatus(HttpStatus.NOT_FOUND.value());
        return "meters/notfound";
    }

    model.addAttribute("meter", meter);
    return "meters/details";
}

但使用此解决方案,我必须为许多控制器方法(如编辑、删除)复制前 5 行。

有没有一种优雅的方法来防止这些行多次重复?

【问题讨论】:

    标签: java spring spring-mvc


    【解决方案1】:

    解决方案比想象的要简单得多。可以使用一个通用的ResourceNotFoundException,定义如下:

    public class ResourceNotFoundException extends RuntimeException { }
    

    然后可以使用ExceptionHandler 注解处理每个控制器中的错误:

    class MeterController {
        // ...
        @ExceptionHandler(ResourceNotFoundException.class)
        @ResponseStatus(HttpStatus.NOT_FOUND)
        public String handleResourceNotFoundException() {
            return "meters/notfound";
        }
    
        // ...
    
        @RequestMapping(value = "/{number}/edit", method = RequestMethod.GET)
        public String viewEdit(@PathVariable("number") final Meter meter,
                               final Model model) {
            if (meter == null) throw new ResourceNotFoundException();
    
            model.addAttribute("meter", meter);
            return "meters/edit";
        }
    }
    

    每个控制器都可以为ResourceNotFoundException 定义自己的ExceptionHandler

    【讨论】:

    • 呃,没有。这并没有真正按预期工作,因为在控制器中进行此类异常处理时,当找不到meter 时响应的 HTTP 状态仍将是200 而不是404。您确实为用户显示了一个不错的页面,但浏览器会收到一个响应,表明请求已成功处理。
    • 感谢您的提示。我刚刚重新检查了我的代码,你是对的。 ResponseStatus 注释应该放在异常处理程序上。我修正了我的答案。
    • 这在我的示例项目中确实有效!但是 - 方法 handleResourceNotFoundException 将如何访问实际的 ResourceNotFoundException 实例对象(我可以给出细节以在错误页面上显示)?
    • 这很容易实现:只需将 Exception 添加到签名中,如下所示:handleResourceNotFoundException(ResourceNotFoundException e)
    【解决方案2】:

    修改了您的 web.xml 文件。使用以下代码。

    <display-name>App Name </display-name>
    <error-page>
    <error-code>500</error-code>
    <location>/error500.jsp</location>
    </error-page>
    
    <error-page>
    <error-code>404</error-code>
    <location>/error404.jsp</location>
    </error-page>
    

    通过以下代码访问它。

    response.sendError(508802,"Error Message");
    

    现在在 web.xml 中添加此代码。

    <error-page>
    <error-code>508802</error-code>
    <location>/error500.jsp</location>
    </error-page>
    

    【讨论】:

    • 如果我想为所有 XyzNotFound 案例提供 一个 自定义错误页面,这将很有用。但我想要的是针对多个不同域类的多个自定义错误页(一个域 class=> 一个错误页)。这些可能包括一些提示,供用户进一步查看。我澄清了我的问题。
    • 使用您编辑的答案,我将无法再返回正确的错误状态代码(即 404)。
    【解决方案3】:

    您可以像下面这样在 web.xml 中映射错误代码

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

    现在您可以创建一个控制器来映射在发现任何这些错误时命中的 url。

    @Controller
    public class HTTPErrorHandler{
    
        String path = "/error";
    
        @RequestMapping(value="/404")
        public String error404(){
           // DO stuff here 
            return path+"/404";
        }
        }
    

    完整示例见my tutorial about this

    【讨论】:

    • 这不能回答问题。 “对不同域类型的查询应该会导致不同的错误页面。”
    • @Ekansh 感谢您的回答和链接在一段时间内寻找此答案 +1
    • Don't be a spammer,拜托。我修正了你的答案,因为这似乎是一个实际的回答尝试,但在未来,你自己公开你的链接。
    【解决方案4】:

    100% 免费 xml 的简单答案:

    1. 设置 DispatcherServlet 的属性

      public class SpringMvcInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
      
      @Override
      protected Class<?>[] getRootConfigClasses() {
          return new Class[] { RootConfig.class  };
      }
      
      @Override
      protected Class<?>[] getServletConfigClasses() {
          return new Class[] {AppConfig.class  };
      }
      
      @Override
      protected String[] getServletMappings() {
          return new String[] { "/" };
      }
      
      //that's important!!
      @Override
      protected void customizeRegistration(ServletRegistration.Dynamic registration) {
          boolean done = registration.setInitParameter("throwExceptionIfNoHandlerFound", "true"); // -> true
          if(!done) throw new RuntimeException();
      }
      

      }

    2. 创建@ControllerAdvice:

      @ControllerAdvice
      public class AdviceController {
      
      @ExceptionHandler(NoHandlerFoundException.class)
      public String handle(Exception ex) {
          return "redirect:/404";
      }
      
      @RequestMapping(value = {"/404"}, method = RequestMethod.GET)
      public String NotFoudPage() {
          return "404";
      
      }
      

      }

    3. 创建包含任何内容的 404.jsp 页面

    就是这样。

    【讨论】:

    • 与其他一些答案一样:这不能回答问题。 “对不同域类型的查询应该会导致不同的错误页面。” - 阅读问题的标题不足以完全理解问题......
    • 从 spring 4.2 RC 3 开始,有一种更简洁的方法来设置throwExceptionIfNoHandlerFound。详情here.
    • 非其他方法工作。但它对我有用。 注意: 必须使用 servlate api 3.10 lower servlet api 不起作用。
    【解决方案5】:

    您应该关注这篇文章,您可以在其中找到有关 Spring MVC 项目中异常处理的详细信息。

    spring-mvc-exception-handling

    @ControllerAdvice 在这种情况下可能会对您有所帮助

    【讨论】:

      【解决方案6】:

      我们可以在 web.xml 文件中添加以下代码行,并在项目的根目录中引入一个名为 errorPage.jsp 的新 jsp 文件来完成需求。

      <error-page>
          <error-code>400</error-code>
          <location>/errorPage.jsp</location>
      </error-page>
      <error-page>
          <error-code>404</error-code>
          <location>/errorPage.jsp</location>
      </error-page>
      <error-page>
          <error-code>500</error-code>
          <location>/errorPage.jsp</location>
      </error-page>
      

      【讨论】:

      • 这不能回答问题。 “对不同域类型的查询应该会导致不同的错误页面。”
      【解决方案7】:

      我还需要不使用org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer

      根据org.springframework.web.servlet.DispatcherServlet.setThrowExceptionIfNoHandlerFound(boolean):“请注意,如果使用 DefaultServletHttpRequestHandler,则请求将始终转发到默认 servlet,并且在这种情况下永远不会抛出 NoHandlerFoundException。”

      https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/DispatcherServlet.html#setThrowExceptionIfNoHandlerFound-boolean-

      之前

      @Configuration
      @EnableWebMvc
      @ComponentScan(basePackages = "com.foo.web")
      public class WebMvcConfiguration extends WebMvcConfigurerAdapter {
      
        @Override
        public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
          configurer.enable();
        }
      
        // ...
      }
      

      之后

      @Configuration
      @EnableWebMvc
      @ComponentScan(basePackages = "com.foo.web")
      public class WebMvcConfiguration extends WebMvcConfigurerAdapter {
      
        @Override
        public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        }
      
        // ...
      }
      

      【讨论】:

      • 实际上你的空体覆盖等于WebMvcConfigurerAdapter中这个方法的默认实现,所以你可以完全删除被覆盖的方法。链接到throwExceptionIfNoHandlerFound 属性真的很有帮助,谢谢。
      【解决方案8】:

      我正在处理一个 Netbeans 项目。我在 web.xml 中添加了以下几行。它仅在我提供 WEB-INF 文件夹的路径时才有效,如下所示。

          <error-page>
              <error-code>404</error-code>
              <location>/WEB-INF/view/common/errorPage.jsp</location>
          </error-page>
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-06-23
        • 2014-11-30
        • 2014-05-15
        • 2012-04-24
        • 2017-08-07
        • 1970-01-01
        • 2016-04-25
        相关资源
        最近更新 更多