404 переадресация ошибок в Spring с помощью java config

Как вы знаете, в XML способ его настройки:

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

Но я не нашел способ сделать это в конфигурации Java. Первый способ, которым я пытался, был:

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

И он, похоже, работает, но он имеет проблемы с поиском ресурсов.

Есть ли способ сделать это?

Ответ 1

В Spring Framework существует несколько способов передачи исключений (и, в частности, ошибки 404). Вот ссылка .

  • Во-первых, вы можете использовать тег error-page в web.xml и настроить страницу с ошибкой. Вот пример .
  • Во-вторых, вы можете использовать один @ExceptionHandler для всех контроллеров, например:

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

    Чтобы это сработало, установите для свойства throwExceptionIfNoHandlerFound значение true для DispatcherServlet в web.xml:

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

    Вы также можете передать некоторые объекты в окно ошибок, см. javadoc.

Ответ 2

Самое чистое решение, так как 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;
    }
}

Затем вы можете использовать глобальный @ControllerAdvice (класс, аннотированный с помощью @ControllerAdvice), как описано в справочных документах. В рамках совета вы можете обрабатывать NoHandlerFoundException с помощью @ExceptionHandler, как описано здесь.

Это может выглядеть примерно так:

@ControllerAdvice
public class NoHandlerFoundControllerAdvice {

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

}

Ответ 3

Используйте инициализацию контейнера сервлетов на основе кода, как описано в методе doc, и переопределите метод 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);
    }
}    

Затем создайте обработчик исключений:

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

Не забудьте использовать аннотацию @EnableWebMvc в файле конфигурации Spring:

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

Ответ 4

В вашем классе веб-конфигурации

@Configuration
public class WebConfig extends WebMvcConfigurerAdapter 

Объявите 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);
    }
  };
}

Добавьте указанные html файлы (401.html.etc) в папку /src/main/resources/static/.

Надеюсь, что это поможет

Ответ 5

Простой ответ для 100% бесплатного xml:

  • Задайте свойства для 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();
        }
    
    }
    
  • Создать @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";
    
        }
    }
    

Ответ 6

Для конфигурации Java существует метод setThrowExceptionIfNoHandlerFound(boolean throwExceptionIfNoHandlerFound) в DispatcherServlet. Установив его на true, я думаю, вы делаете то же самое

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

то вы можете использовать этот NoHandlerFoundException.class в рекомендации контроллера, как указано в приведенном выше ответе

он будет как-то

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

Ответ 7

Предлагаемое решение в комментариях выше действительно работает:

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

Ответ 8

Решение для Spring 5 и Thymeleaf 3.

В MyWebInitializer включите 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;
        }
    }

Создайте совет контроллера с помощью @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;
    }
}

Создайте шаблон ошибки 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>