使用java配置在Spring中重定向404错误

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/23574869/
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-14 00:04:02  来源:igfitidea点击:

404 error redirect in Spring with java config

javaspringspring-mvc

提问by Carlos López

As you know, in XML, the way to configure this is:

如您所知,在 XML 中,配置它的方法是:

<error-page>
    <error-code>404</error-code>
    <location>/my-custom-page-not-found.html</location>
</error-page>

But I haven't found a way to do it in Java config. The first way I tried was:

但是我还没有找到在 Java 配置中做到这一点的方法。我尝试的第一种方法是:

@RequestMapping(value = "/**")
public String Error(){
    return "error";
}

And it appeared to work, but it has conflicts retrieving the resources.

它似乎有效,但在检索资源时存在冲突。

Is there a way to do it?

有没有办法做到这一点?

回答by Eugene

In Spring Framework, there are number of ways of handing exceptions (and particularly 404 error). Here is a documentation link.

在 Spring Framework 中,有多种处理异常(尤其是 404 错误)的方法。这是一个文档链接

  • First, you can still use error-pagetag in web.xml, and customize error page. Here is an example.
  • Second, you can use one @ExceptionHandlerfor all controllers, like this:

    @ControllerAdvice
    public class ControllerAdvisor {
    
         @ExceptionHandler(NoHandlerFoundException.class)
         public String handle(Exception ex) {
    
            return "404";//this is view name
        }
    }
    

    For this to work, set throwExceptionIfNoHandlerFoundproperty to true for DispatcherServletin web.xml:

    <init-param>
        <param-name>throwExceptionIfNoHandlerFound</param-name>
        <param-value>true</param-value>
    </init-param>
    

    You can also pass some objects to error view, see javadocfor this.

  • 首先,您仍然可以error-page在 web.xml 中使用标签,并自定义错误页面。这是一个例子
  • 其次,您可以@ExceptionHandler为所有控制器使用一个,如下所示:

    @ControllerAdvice
    public class ControllerAdvisor {
    
         @ExceptionHandler(NoHandlerFoundException.class)
         public String handle(Exception ex) {
    
            return "404";//this is view name
        }
    }
    

    为此,在 web.xml中将throwExceptionIfNoHandlerFound属性设置为 true DispatcherServlet

    <init-param>
        <param-name>throwExceptionIfNoHandlerFound</param-name>
        <param-value>true</param-value>
    </init-param>
    

    您还可以将一些对象传递给错误视图,请参阅javadoc

回答by leshkin

Use code-based Servlet container initialization as described in the docand override registerDispatcherServletmethod to set throwExceptionIfNoHandlerFoundproperty to true:

使用文档中描述的基于代码的 Servlet 容器初始化并覆盖registerDispatcherServlet方法将throwExceptionIfNoHandlerFound属性设置为 true:

public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return null;
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[] { WebConfig.class };
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }

    @Override
    protected void registerDispatcherServlet(ServletContext servletContext) {
        String servletName = getServletName();
        Assert.hasLength(servletName, "getServletName() may not return empty or null");

        WebApplicationContext servletAppContext = createServletApplicationContext();
        Assert.notNull(servletAppContext,
            "createServletApplicationContext() did not return an application " +
                    "context for servlet [" + servletName + "]");

        DispatcherServlet dispatcherServlet = new DispatcherServlet(servletAppContext);

        // throw NoHandlerFoundException to Controller
        dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);

        ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
        Assert.notNull(registration,
            "Failed to register servlet with name '" + servletName + "'." +
                    "Check if there is another servlet registered under the same name.");

        registration.setLoadOnStartup(1);
        registration.addMapping(getServletMappings());
        registration.setAsyncSupported(isAsyncSupported());

        Filter[] filters = getServletFilters();
        if (!ObjectUtils.isEmpty(filters)) {
            for (Filter filter : filters) {
                registerServletFilter(servletContext, filter);
            }
        }

        customizeRegistration(registration);
    }
}    

Then create an exception handler:

然后创建一个异常处理程序:

@ControllerAdvice
public class ExceptionHandlerController {
    @ExceptionHandler(Exception.class)
    public String handleException(Exception e) {
        return "404";// view name for 404 error
    }   
}

Don't forget about using @EnableWebMvc annotation on your Spring configuration file:

不要忘记在Spring 配置文件上使用 @EnableWebMvc 注释:

@Configuration
@EnableWebMvc
@ComponentScan(basePackages= {"org.project.etc"})
public class WebConfig extends WebMvcConfigurerAdapter {
    ...
}

回答by Dhruv Pal

For Java config there is a method setThrowExceptionIfNoHandlerFound(boolean throwExceptionIfNoHandlerFound)in DispatcherServlet. By settting it to trueI guess you are doing same thing

对于 Java 配置,setThrowExceptionIfNoHandlerFound(boolean throwExceptionIfNoHandlerFound)DispatcherServlet. 通过将其设置为true我猜你在做同样的事情

<init-param>
    <param-name>throwExceptionIfNoHandlerFound</param-name>
    <param-value>true</param-value>
</init-param>

then you can can this NoHandlerFoundException.classin controller advice as stated in above answer

那么您可以NoHandlerFoundException.class在控制器建议中进行此操作,如上述答案所述

it will be like something

它会像什么

public class WebXml implements WebApplicationInitializer{

    public void onStartup(ServletContext servletContext) throws ServletException {
        WebApplicationContext context = getContext();
        servletContext.addListener(new ContextLoaderListener(context));


        DispatcherServlet dp =  new DispatcherServlet(context);
        dp.setThrowExceptionIfNoHandlerFound(true);

        ServletRegistration.Dynamic dispatcher = servletContext.addServlet("DispatcherServlet", dp);
        dispatcher.setLoadOnStartup(1);
        dispatcher.addMapping(MAPPING_URL);
    }
}

回答by Christian Rudolph

The most clean solution since spring 4.2 RC3 is using the new createDispatcherServlethook within the class extending AbstractDispatcherServletInitializer(or indirectly through extending AbstractAnnotationConfigDispatcherServletInitializer) like this:

自 spring 4.2 RC3 以来最干净的解决方案是createDispatcherServlet在类扩展中使用新钩子AbstractDispatcherServletInitializer(或间接通过扩展AbstractAnnotationConfigDispatcherServletInitializer),如下所示:

public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return null;
    }

    /* ... */

    @Override
    protected DispatcherServlet createDispatcherServlet(WebApplicationContext servletAppContext) {
        final DispatcherServlet dispatcherServlet = super.createDispatcherServlet(servletAppContext);
        dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
        return dispatcherServlet;
    }
}

Then you can use a global @ControllerAdvice(a class that is annotated with @ControllerAdvice) as described in the reference docs. Within the advice you can handle the NoHandlerFoundExceptionwith an @ExceptionHandleras described here.

然后,您可以使用参考文档中所述的全局@ControllerAdvice(用 注释的类@ControllerAdvice)。在通知您可以处理与所描述这里NoHandlerFoundException@ExceptionHandler

This could look something like this:

这可能看起来像这样:

@ControllerAdvice
public class NoHandlerFoundControllerAdvice {

    @ExceptionHandler(NoHandlerFoundException.class)
    public ResponseEntity<String> handleNoHandlerFoundException(NoHandlerFoundException ex) {
        // prepare responseEntity
        return responseEntity;
    }

}

回答by Jaffadog

The solution proposed in comments abovereally works:

上面评论中提出的解决方案确实有效:

@Override
protected void customizeRegistration(ServletRegistration.Dynamic registration)
{
  registration.setInitParameter("throwExceptionIfNoHandlerFound", "true");
}

回答by SerdukovAA

Simple answer for 100% free xml:

100% 免费 xml 的简单答案:

  1. Set properties for 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[] { "/" };
        }
    
        @Override
        protected void customizeRegistration(ServletRegistration.Dynamic registration) {
            boolean done = registration.setInitParameter("throwExceptionIfNoHandlerFound", "true"); // -> true
            if(!done) throw new RuntimeException();
        }
    
    }
    
  2. Create @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";
    
        }
    }
    
  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[] { "/" };
        }
    
        @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";
    
        }
    }
    

回答by tharindu_DG

In your web configuration class,

在您的 Web 配置类中,

@Configuration
public class WebConfig extends WebMvcConfigurerAdapter 

Declare a bean as follows,

声明一个bean如下,

@Bean
public EmbeddedServletContainerCustomizer containerCustomizer() {

  return new EmbeddedServletContainerCustomizer() {
    @Override
    public void customize(ConfigurableEmbeddedServletContainer container)       
    {
      ErrorPage error401Page = new ErrorPage(HttpStatus.UNAUTHORIZED, "/401.html");
      ErrorPage error404Page = new ErrorPage(HttpStatus.NOT_FOUND, "/404.html");
      ErrorPage error500Page = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/500.html");

      container.addErrorPages(error401Page,error404Page,error500Page);
    }
  };
}

Add the mentioned html files(401.html.etc) to /src/main/resources/static/folder.

将提到的 html 文件(401.html.etc)添加到/src/main/resources/static/文件夹。

Hope this helps

希望这可以帮助

回答by Jan Bodnar

A solution for Spring 5 and Thymeleaf 3.

Spring 5 和 Thymeleaf 3 的解决方案。

In MyWebInitializer, enable exception throwing with setThrowExceptionIfNoHandlerFound(). We need to do casting to DispatcherServlet.

在 中MyWebInitializer,使用 启用异常抛出setThrowExceptionIfNoHandlerFound()。我们需要对DispatcherServlet.

@Configuration
public class MyWebInitializer extends
        AbstractAnnotationConfigDispatcherServletInitializer {

        ...

        @Override
        protected FrameworkServlet createDispatcherServlet(WebApplicationContext servletAppContext) {
            var dispatcher = (DispatcherServlet) super.createDispatcherServlet(servletAppContext);
            dispatcher.setThrowExceptionIfNoHandlerFound(true);
            return dispatcher;
        }
    }

Create a controller advice with @ControllerAdviceand add error message to the ModealAndView.

创建控制器建议@ControllerAdvice并将错误消息添加到ModealAndView.

@ControllerAdvice
public class ControllerAdvisor {

    @ExceptionHandler(NoHandlerFoundException.class)
    public ModelAndView handle(Exception ex) {

        var mv = new ModelAndView();
        mv.addObject("message", ex.getMessage());
        mv.setViewName("error/404");

        return mv;
    }
}

Create 404 error template, which displays the error message. Based on my configuration, the file is src/main/resources/templates/error/404.html.

创建 404 错误模板,显示错误信息。根据我的配置,文件是src/main/resources/templates/error/404.html.

<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <title>Resource not found</title>
</head>
<body>

<h2>404 - resource not found</h2>

<p>
    <span th:text="${message}" th:remove="tag"></span>
</p>

</body>
</html>

For completeness, I add the Thymeleaf resolver configuration. We configure the Thymeleaf templates to be in templatesdirectory on the classpath.

为了完整起见,我添加了 Thymeleaf 解析器配置。我们将 Thymeleaf 模板配置templates在类路径上的目录中。

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = {"com.zetcode"})
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private ApplicationContext applicationContext;

    ...

    @Bean
    public SpringResourceTemplateResolver templateResolver() {

        var templateResolver = new SpringResourceTemplateResolver();

        templateResolver.setApplicationContext(applicationContext);
        templateResolver.setPrefix("classpath:/templates/");
        templateResolver.setSuffix(".html");

        return templateResolver;
    }
    ...
}

回答by bpawlowski

In springboot it is even simplier. Because of Spring autoconfiguration stuff, spring creates a bean org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties. This class is annotated with @ConfigurationProperties(prefix = "spring.mvc")and therefore it will seek for properties with spring.mvc prefix.

在 springboot 中,它甚至更简单。由于 Spring 自动配置的东西,spring 创建了一个 bean org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties。此类带有注释,@ConfigurationProperties(prefix = "spring.mvc")因此它将寻找带有 spring.mvc 前缀的属性。

Part from javadoc:

部分来自 javadoc:

Annotation for externalized configuration. Add this to a class definition or a
* @Bean method in a @Configuration class if you want to bind and validate
* some external Properties (e.g. from a .properties file).

You just have to add to your i.e. application.propertiesfile following properties:

您只需将application.properties以下属性添加到您的 ie文件中:

 spring.mvc.throwExceptionIfNoHandlerFound=true
 spring.resources.add-mappings=false //this is for spring so it won't return default handler for resources that not exist

and add exception resolver as follows:

并添加异常解析器如下:

@ControllerAdvice
public class ExceptionResponseStatusHandler {
    @ExceptionHandler(NoHandlerFoundException.class)
    public ModelAndView handle404() {
        var out = new ModelAndView();
        out.setViewName("404");//you must have view named i.e. 404.html
        return out;
    }
}