This article will explain, when the exception scenario, such as 404 request URL does not exist, 403 has no authority, 500 server exception, how can we handle

I. Environment construction

First of all, it is possible to build a Web application to continue the subsequent test. It is relatively simple to build a Web application with SpringBoot.

Create a Maven project with the following POM file

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

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
    <java.version>1.8</java.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.45</version>
    </dependency>
</dependencies>

<build>
    <pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </pluginManagement>
</build>
<repositories>
    <repository>
        <id>spring-milestones</id>
        <name>Spring Milestones</name>
        <url>https://repo.spring.io/milestone</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
</repositories>
Copy the code

Again, the general process, poM dependencies are done, write a program entry

/**
 * Created by @author yihui in 15:26 19/9/13.
 */
@SpringBootApplication
public class Application {
    public static void main(String[] args) { SpringApplication.run(Application.class); }}Copy the code

II. Configure the abnormal page

In the SpringBoot project, a default exception handling page is provided. What happens when we want to use a custom 404,500 page?

1. Configure abnormal pages by default

By default, configuring exception pages is very simple. Under the resource path, create an Error directory and add 400.html, 500HTML pages

Note that the example demo does not use a template engine, so our exception page is placed in the static directory. If you use a template engine such as FreeMaker, you can place the error template page in the template directory

Next, to actually test whether this works, let’s first define a service where server 500 is possible

@Controller
@RequestMapping(path = "page")
public class ErrorPageRest {

    @ResponseBody
    @GetMapping(path = "divide")
    public int divide(int sub) {
        System.out.println("divide1");
        return 1000/ sub; }}Copy the code

Request a non-existent URL to return the 400.html page we defined

<html>
<head>
    <title>404 pages</title>
</head>
<body>
<h3>Page does not exist</h3>
</body>
</html>
Copy the code

Request a server 500 exception and return the 500.html page we defined

<html>
<head>
    <title>500 pages</title>
</head>
<body>
<h2 style="color: red;">Server is abnormal!!</h2>
</body>
</html>
Copy the code

2. BasicErrorController

Look at the use of the above relatively simple, naturally there will be a question, this exception page is how to return?

From project startup log, pay attention to the RequestMappingHandlerMapping

It can be found that there is a /error path that is not defined by ourselves. From the naming point of view, this is probably the Controller -> BasicErrorController specially used to handle exceptions. Part of the code is as follows

@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {

	@Override
	public String getErrorPath(a) {
		return this.errorProperties.getPath();
	}

	@RequestMapping(produces = "text/html")
	public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
		HttpStatus status = getStatus(request);
		Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(
				request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
		response.setStatus(status.value());
		ModelAndView modelAndView = resolveErrorView(request, response, status, model);
		return(modelAndView ! =null)? modelAndView :new ModelAndView("error", model);
	}

	@RequestMapping
	@ResponseBody
	public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
		Map<String, Object> body = getErrorAttributes(request,
				isIncludeStackTrace(request, MediaType.ALL));
		HttpStatus status = getStatus(request);
		return newResponseEntity<>(body, status); }}Copy the code

In this Controller, there’s an interface for returning web pages, and an interface for returning Json strings; We should have used the first one before, so in what scenarios will we use the second one?

  • By specifying the request headerAcceptTo specify that we only want the return of JSON

3. Customize the exception controller

The custom exception controller requires us to implement the ErrorController interface. Note that the custom exception controller will overwrite the default exception page we set earlier

(HERE I always return json, if using thymeleaf, return the corresponding page string)

// Capture a 404 page
public class HttpErrorHandler implements ErrorController {

    private final static String ERROR_PATH = "/error";

    /**
     * Supports the HTML Error View
     *
     * @param request
     * @return* /
    @RequestMapping(value = ERROR_PATH)
    public String errorHtml(HttpServletRequest request) {
        Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
        if(statusCode == 401) {return  "{ \"code\": \"401\"}";
        }else if(statusCode == 404) {return "{ \"code\": \"404\"}";
        }else if(statusCode == 403) {return "{ \"code\": \"403\"}";
        }else{
            return "{ \"code\": \"500\"}"; }}@Override
    public String getErrorPath(a) {
        returnERROR_PATH; }}Copy the code

But if and else, not to mention the fact that there’s a bunch of if and else nested, and it’s not very elegant to read, so what are the strategies for improving that

  • Start by defining an interface class that represents the JSON returned by the different status codes
public interface StatusReturnJson {
    String Json(a);
}
Copy the code
  • Next we leave all the cases for the different state codes to the enumeration class
public enum  HttpStatusEnum implements StatusReturnJson{
    error_404{
        public String Json(a){
            return "{ \"code\": \"404\"}";
        }
    },

    error_401{
        public String Json(a){
            return "{ \"code\": \"401\"}";
        }
    },
    error_403{
        public String Json(a){
            return "{ \"code\": \"403\"}";
        }
    },
    error_500{
        public String Json(a){
            return "{ \"code\": \"500\"}"; }}},Copy the code

The next call is very simple, just a single line call, if, else also disappeared

@RestController
public class HttpErrorController implements ErrorController {

    private final static String ERROR_PREFIX = "error_";
    private final static String ERROR_PATH = "/error";


    @RequestMapping(value = ERROR_PATH)
    public String errorJson(HttpServletRequest request) {
        Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
        String key = ERROR_PREFIX + statusCode;
        return HttpStatusEnum.valueOf(key).Json();
    }

    @Override
    public String getErrorPath(a) {
        returnERROR_PATH; }}Copy the code

4. Summary

The content of this article is relatively simple, summed up in two sentences as follows

  • The custom exception page is named according to the HTTP status code and placed in/errordirectory
  • In case of an exception, the page corresponding to the exception is found and returned according to the returned HTTP status code
  • Custom exception controller

Reference:

Configuration of 404 and 500 exception pages in the Web part of the SpringBoot tutorial series

Stop with the if/else