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 header
Accept
To 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
/error
directory - 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