Home > Mobile >  Problem trying to display custom error pages with Spring Boot
Problem trying to display custom error pages with Spring Boot

Time:10-28

I'm maintaining a Spring Boot Application that uses Swagger to define Rest web services and uses Maven for dependency management. It uses an application.yml file for properties. By default, when an error occurs, a Whitelabel page is displayed in any browser.

The parent in the application's pom.xml is defined as follows:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.1.RELEASE</version>
    <relativePath /> <!-- lookup parent from repository -->
</parent>

I created HTML pages in the resources/templates/error and resources/public/error each for a generic error, a 404 error, a 401 error and a 500 error.

Afterwards, I tried the following solutions without success:

  1. According to https://www.baeldung.com/spring-boot-custom-error-page, I must follow these steps:

1.1. Disable the Whitelabel display from the properties file or from the main class. I have chosen to do it from the main class:

@SpringBootApplication(scanBasePackages = "com.mycompanyname")
@EnableConfigurationProperties(AppProperties.class)
@EnableAutoConfiguration(exclude = {ErrorMvcAutoConfiguration.class}) // <- Like this!
public class BuySellBackendApplication {

    public static void main(String[] args) {
        SpringApplication.run(BuySellBackendApplication.class, args);
    }
}

1.2. Define a custom error controller that implements the ÈrrorController interface. Mine is defined as follows:

@Controller
public class BazaarErrorController implements ErrorController {

    @Override
    public String getErrorPath() {
        return "/custom-error";
    }

    @RequestMapping("/custom-error")
    public String handleError() {
        return "error";
    }
}

1.3. Add the path to the error mapping defined from the properties file. As I am using a yml file, the property is added as follows:

server:
   error:
      path: "/custom-error"

The desired result is to display the generic error page I defined. However, the obtained result is an error page defined by Tomcat. Moreover, Tomcat's error page is trigged from the following class in the application, that uses Spring Security:

public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {

    private static final Logger logger = LoggerFactory.getLogger(RestAuthenticationEntryPoint.class);

    @Override
    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        logger.error("Responding with unauthorized error. Message - {}", e.getMessage());
        httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getLocalizedMessage());  // <- This line of code
    }
}

In those circumstances, even using the @RequestBody annotation and/or make return a different page according to the obtained HTTP error code is useless.

I cannot erase the lines of code of the authentication entry point due to the company's requirements. Also, instead of redirecting me to my error page, it opens the browser's download dialog.

  1. According to https://www.logicbig.com/tutorials/spring-framework/spring-boot/servlet-error-handling-outside-mvc.html, I must define an error page register. Spring Web provides the ErrorPageRegistrar interface to do this:
@Bean
public ErrorPageRegistrar errorPageRegistrar() {
    return registry -> {
        ErrorPage e404=new ErrorPage(HttpStatus.NOT_FOUND, "/error/"   HttpStatus.NOT_FOUND.value());
        ErrorPage e401=new ErrorPage(HttpStatus.UNAUTHORIZED, "/error/"   HttpStatus.UNAUTHORIZED.value());
        ErrorPage e500=new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error/"   HttpStatus.INTERNAL_SERVER_ERROR.value());
        ErrorPage egeneric=new ErrorPage("/error/error");
        registry.addErrorPages(e401, e404, e500, egeneric);
    };
}

The obtained result is the same as overriding the Authentication entry point and go to the browser's download dialog in solution 1. Also, it's not very clear if the String argument in the ErrorPage's constructor is a physical HTML file or a servlet mapping.

  1. Define an error view resolver using Spring's ErrorViewResolver interface as described in https://forketyfork.medium.com/how-to-customize-error-page-selection-logic-in-spring-boot-8ea1a6ae122d:
@Configuration
public class ErrorPageConfig {
    private static final Logger logger = LoggerFactory.getLogger(ErrorPageConfig.class);
    @Bean
    public ErrorViewResolver errorViewResolver(ApplicationContext context, ResourceProperties properties){
        ErrorViewResolver resolver=(request, status, model) -> {
            String pathFormat="error/%d";
            String path="";
            switch(status.value()) {
                case 401: case 404: case 500:
                    path=String.format(pathFormat, status.value());
                    break;
                default:
                    logger.info("Codigo de error obtenido {}", status.value());
                    path="error/error";
                    break;
            }
            return new ModelAndView(path);
        };
        return resolver;
    }
}

It gives me the same result as solution 2.

How can I resolve this problem? Thanks in advance

CodePudding user response:

SOLVED The solution is combining some steps of the first and the third solution as follows:

  1. Disable the Whitelabel display from the properties file or from the main class. See the question above for details.

  2. Define the error view resolver completely as in solution 3.

  3. The HTML pages must be en resources/templates

I hope this helps for everybody who needs it.

CodePudding user response:

isable the white label error page entirely, by setting the server.error.whitelabel.enabled property to false:

server.error.whitelabel.enabled=false

we have to create an error controller bean that'll replace the default error page. @Controller public class MyErrorController implements ErrorController {

 @RequestMapping("/error")
 public String handleError() {
    //do something like logging
    return "error";
   }
}

then add this property server.error.path=/error

you can create error handller like this:

 @RequestMapping("/error")
 public String handleError(HttpServletRequest request) {
 Object status = 
 request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);

  if (status != null) {
    Integer statusCode = Integer.valueOf(status.toString());

    if(statusCode == HttpStatus.NOT_FOUND.value()) {
        return "error-404";
    }
    else if(statusCode == HttpStatus.INTERNAL_SERVER_ERROR.value()) {
        return "error-500";
    }
   }
  return "error";
}

Note:- error-404 ,error-500 are custom html page

  • Related