Ошибка 404 в Spring (java config / no web.xml) - PullRequest
0 голосов
/ 06 марта 2019

Попытка предоставить пользовательскую страницу ошибки 404 в веб-приложении, которое, насколько мне известно, использует Java Config (таким образом, не web.xml).

У нас есть следующие версии связанных библиотек: spring ("5.1.2.RELEASE"), spring-security ("5.1.1.RELEASE").

Отказ от ответственности

Я проверил различные подходы здесь, в StackOverflow. пожалуйста не предлагать результаты для web.xml, Thymeleaf или Spring Boot. Это не применимо.

Среди прочих; Я пробовал со следующими подходами:

Ни один из них не дал ожидаемого результата (то есть, все еще получая макет веб-сервера по умолчанию и ошибку).

Подход к аннотации контроллера

  • пакет исключений

    package ...;
    
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.servlet.NoHandlerFoundException;
    
    @ControllerAdvice
    public class GlobalExceptionHandler {
    
        // Option A (used as an alternative to option B)
        //@ExceptionHandler(Exception.class)
        //public String handle(Exception ex) {
        //   return "redirect:/404";
        //}
    
        @RequestMapping(value = {"/404"}, method = RequestMethod.GET)
        public String NotFoundPage() {
            return "404";
        }
    
        // Option B (used as an alternative to option A)
        @ExceptionHandler(Exception.class)
        public ResponseEntity<String> handleNoHandlerFoundException(GlobalExceptionHandler ex) {
            ResponseEntity responseEntity = new ResponseEntity<>(new RestClientException("Testing exception"),
                HttpStatus.NOT_FOUND);
            return responseEntity;
        }
    }
    
  • начальный класс

    package ...;
    
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.PropertySource;
    import org.springframework.transaction.annotation.EnableTransactionManagement;
    import org.springframework.web.servlet.config.annotation.EnableWebMvc;
    import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
    
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.http.ResponseEntity;
    import org.springframework.http.HttpStatus;
    import org.springframework.web.servlet.NoHandlerFoundException;
    import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
    
    @Configuration
    @ComponentScan("...")
    @EnableWebMvc
    @EnableTransactionManagement
    @PropertySource("classpath:application.properties")
    public class WebAppConfig extends WebMvcConfigurerAdapter {
    
        @ExceptionHandler({ Exception.class })
        public ResponseEntity<RestClientException> handle(NoHandlerFoundException e) {
            return new ResponseEntity<>(new RestClientException("Testing exception"), HttpStatus.NOT_FOUND);
        }
    
        ...
    
        @Override
        public void addViewControllers(ViewControllerRegistry registry) {
            super.addViewControllers(registry);
            registry.addViewController("/404.jsp").setViewName("404");
        }
    }
    

Существует также класс Initializer (public class Initializer implements WebApplicationInitializer), который, кажется, конфликтует с некоторыми предлагаемыми опциями (определены здесь и здесь ); поэтому класс webapp-init не изменяется.

web.xml подход

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="ROOT" xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
    version="2.4">

<error-page>
  <error-code>404</error-code>
  <location>/error</location>
</error-page>
<error-page>
    <exception-type>java.lang.Exception</exception-type>
    <location>/error</location>
</error-page>

</web-app>

Файлы 404.jsp или 404.html размещены (в настоящее время для целей тестирования во всех следующих местах):

    src/main/resources
    ├── ...
    ├── error
    │   └── 404.html
    ├── public
    │   ├── 404.html
    │   └── error
    │       └── 404.html
    ├── templates
    │   └── 404.html
    └── ...

    src/main/webapp/WEB-INF/
    ├── error.jsp
    ├── tags
    │   └── ...
    └── views
        ├── 404.html
        ├── 404.jsp
        ├── error.jsp
        └── ...

Есть идеи о том, чего не хватает или что не так?

1 Ответ

0 голосов
/ 08 марта 2019

Хотя это и не так ясно, как хотелось бы, это своего рода рабочая версия, по крайней мере, обеспечивающая некоторую настройку страниц ошибок.Это первый подход, но мы надеемся, что он может помочь другим.

Список обработанных исключений не является обширным, он в основном касается 404 ошибок (NoHandlerFoundException) и других типичных ошибок, таких как InternalServerErrorExceptionи NullPointerException, чтобы попытаться поймать их всех в конце с общей ошибкой для всего остального, Exception).

Обратите внимание , что это не покрываетдругие исключения, связанные, например, с плохим синтаксисом в шаблоне JSTL (org.apache.jasper.*; исключения, которые здесь явно не могут быть обнаружены).

Это связанные изменения и дополнения в исходной базе:

CustomSimpleMappingExceptionResolver.java (предоставить общие исключения, но регистрирует подробности)

package ...;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import javax.ws.rs.InternalServerErrorException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.http.HttpStatus;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.NoHandlerFoundException;

public class CustomSimpleMappingExceptionResolver extends SimpleMappingExceptionResolver {

    public CustomSimpleMappingExceptionResolver() {
        // Turn logging on by default
        setWarnLogCategory(getClass().getName());
    }

    @Override
    public String buildLogMessage(Exception e, HttpServletRequest req) {
        return "MVC exception: " + e.getLocalizedMessage();
    }

    @Override
    protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response,
                                              Object handler, Exception ex) {

        // Log exception
        ex.printStackTrace();
        String exceptionCause = ex.toString();
        String exceptionType = ex.getClass().getCanonicalName();

        // Get the ModelAndView to use
        ModelAndView mav = super.doResolveException(request, response, handler, ex);

        // Make more information available to the view - note that SimpleMappingExceptionResolver adds the exception already
        mav.addObject("url", request.getRequestURL());
        mav.addObject("timestamp", new Date());

        ArrayList<String> exceptions404 = new ArrayList<String>(
                Arrays.asList(
                        NoHandlerFoundException.class.getName()
                        )
        );
        ArrayList<String> exceptions500 = new ArrayList<String>(
                Arrays.asList(
                        InternalServerErrorException.class.getName(),
                        NullPointerException.class.getName()
                        )
        );

        String userExceptionDetail = ex.toString();
        String errorHuman = "";
        String errorTech = "";

        if (exceptions404.contains(exceptionType)) {
            errorHuman = "We cannot find the page you are looking for";
            errorTech = "Page not found";
            userExceptionDetail = String.format("The page %s cannot be found", request.getRequestURL());
            mav.setViewName("/error/404");
            mav.addObject("status", HttpStatus.NOT_FOUND.value());
        } else if (exceptions500.contains(exceptionType)) {
            errorHuman = "We cannot currently serve the page you request";
            errorTech = "Internal error";
            userExceptionDetail = "The current page refuses to load due to an internal error";
            mav.setViewName("/error/500");
            mav.addObject("status", HttpStatus.INTERNAL_SERVER_ERROR.value());
        } else {
            errorHuman = "We cannot serve the current page";
            errorTech = "General error";
            userExceptionDetail = "A generic error prevents from serving the page";
            mav.setViewName("/error/generic");
            mav.addObject("status", response.getStatus());
        }

        Exception userException = new Exception(userExceptionDetail);
        mav.addObject("error_human", errorHuman);
        mav.addObject("error_tech", errorTech);
        mav.addObject("exception", userException);
        return mav;
    }
}

WebAppConfig.java (регистрирует обработчик пользовательских исключений в качестве обработчика исключений)

package ...;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.core.env.Environment;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

import java.lang.ClassNotFoundException;
import java.lang.NullPointerException;
import javax.annotation.Resource;
import javax.ws.rs.InternalServerErrorException;
import java.util.Properties;

@Configuration
@ComponentScan("...")
@EnableWebMvc
@EnableTransactionManagement
@PropertySource("classpath:application.properties")
public class WebAppConfig extends WebMvcConfigurerAdapter {

    @Resource
    private Environment env;

    // ...

    @Bean
    HandlerExceptionResolver customExceptionResolver () {
        CustomSimpleMappingExceptionResolver resolver = new CustomSimpleMappingExceptionResolver();
        Properties mappings = new Properties();
        // Mapping Spring internal error NoHandlerFoundException to a view name
        mappings.setProperty(NoHandlerFoundException.class.getName(), "/error/404");
        mappings.setProperty(InternalServerErrorException.class.getName(), "/error/500");
        mappings.setProperty(NullPointerException.class.getName(), "/error/500");
        mappings.setProperty(ClassNotFoundException.class.getName(), "/error/500");
        mappings.setProperty(Exception.class.getName(), "/error/generic");
        resolver.setExceptionMappings(mappings);
        // Set specific HTTP codes
        resolver.addStatusCode("404", HttpStatus.NOT_FOUND.value());
        resolver.addStatusCode("500", HttpStatus.INTERNAL_SERVER_ERROR.value());
        resolver.setDefaultErrorView("/error/generic");
        resolver.setDefaultStatusCode(200);
        // This resolver will be processed before the default ones
        resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
        resolver.setExceptionAttribute("exception");
        return resolver;
    }

    // ...

    @Bean
    public InternalResourceViewResolver setupViewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/views");
        resolver.setSuffix(".jsp");
        resolver.setExposeContextBeansAsAttributes(true);
        return resolver;
    }

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        super.addViewControllers(registry);
    }
}

Initializer.java (добавление dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);; возможно, не требуется)

package ...;

import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;

public class Initializer implements WebApplicationInitializer {

    public void onStartup(ServletContext servletContext) throws ServletException {
        AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
        ctx.register(WebAppConfig.class);
        servletContext.addListener(new ContextLoaderListener(ctx));
        ctx.setServletContext(servletContext);
        DispatcherServlet dispatcherServlet = new DispatcherServlet(ctx);
        dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);

        // Add the dispatcher servlet mapping manually and make it initialize automatically
        ServletRegistration.Dynamic servlet = servletContext.addServlet("dispatcher", dispatcherServlet);
        servlet.addMapping("/");
        servlet.addMapping("*.png");
        servlet.addMapping("*.jpg");
        servlet.addMapping("*.css");
        servlet.addMapping("*.js");
        servlet.addMapping("*.txt");
        servlet.setLoadOnStartup(1);

        // ...

    }
}

Структура представлений и тегов, связанных с классами ошибок:

    src/main/webapp/WEB-INF/
    ├── tags
    │   └── error.tag
    └── views
        ├── error
        │   ├── 404.jsp
        │   ├── 500.jsp
        └────── generic.jsp

src / main / webapp / WEB-INF / tags / error.tag

<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<!DOCTYPE html>
<head>
    <title>Error page</title>
</head>
<body>
<div class="container">
    <h3><c:out value="${error_human}" /></h3>

    <p><br/><br/></p>

    <div class="panel panel-primary">
        <div class="panel-heading">
            <c:out value="${error_tech}" />
        </div>
        <div class="panel-body">
            <p><c:out value="${exception_message}" /></p>
        </div>
    </div>
</div>
</body>
</html>

src / main / webapp / WEB-INF / views / error / 404.jsp

<%@ page language="java" contentType="text/html; charset=utf-8"
         pageEncoding="utf-8" isErrorPage="true" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib tagdir="/WEB-INF/tags/" prefix="g" %>

<c:set var = "error_human" scope = "session" value = "We cannot find the page you are looking for"/>
<c:set var = "error_tech" scope = "session" value = "Page not found"/>
<c:set var = "exception_message" scope = "session" value = "The current page cannot be found"/>
<g:error />

src / main / webapp / WEB-INF / views / error / 500.jsp

<%@ page language="java" contentType="text/html; charset=utf-8"
         pageEncoding="utf-8" isErrorPage="true" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib tagdir="/WEB-INF/tags/" prefix="g" %>

<c:set var = "error_human" scope = "session" value = "We cannot currently serve the page you request"/>
<c:set var = "error_tech" scope = "session" value = "Internal error"/>
<c:set var = "exception_message" scope = "session" value = "The current page refuses to load due to an internal error"/>
<g:error />

src / main / webapp / WEB-INF / views /error / generic.jsp

<%@ page language="java" contentType="text/html; charset=utf-8"
         pageEncoding="utf-8" isErrorPage="true" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib tagdir="/WEB-INF/tags/" prefix="g" %>

<c:set var = "error_human" scope = "session" value = "We cannot serve the current page"/>
<c:set var = "error_tech" scope = "session" value = "General error"/>
<c:set var = "exception_message" scope = "session" value = "A generic error prevents from serving the page"/>
<g:error />
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...