Article reference
spring-boot-demo-exception-handler
github jump
We will encounter many exceptions in our project. We should catch exceptions in time, but inevitably, there will be many unexpected exceptions. Therefore, we should customize the global exceptions and deal with these exception information uniformly
1. Project construction
Create a new SpringBoot project and introduce lombok, web, thymeleaf and other dependencies
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <!--There is also a dependency on hot deployment. You can add it or not--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency> </dependencies>
2. Unified return results
2.1 unified result return class
Just take a few glances. It's easy to use. The Status is below
@Data public class ApiResponse { /** * Status code */ private Integer code; /** * Return content */ private String message; /** * Return data */ private Object data; /** * non-parameter constructor */ private ApiResponse() { } /** * All parameter constructor * * @param code Status code * @param message Return content * @param data Return data */ private ApiResponse(Integer code, String message, Object data) { this.code = code; this.message = message; this.data = data; } /** * Construct a custom API return * * @param code Status code * @param message Return content * @param data Return data * @return ApiResponse */ public static ApiResponse of(Integer code, String message, Object data) { return new ApiResponse(code, message, data); } /** * Construct a successful API return with data * * @param data Return data * @return ApiResponse */ public static ApiResponse ofSuccess(Object data) { return ofStatus(Status.OK, data); } /** * Construct an API return of a successful and customized message * * @param message Return content * @return ApiResponse */ public static ApiResponse ofMessage(String message) { return of(Status.OK.getCode(), message, null); } /** * Construct a stateful API return * * @param status Status {@ link Status} * @return ApiResponse */ public static ApiResponse ofStatus(Status status) { return ofStatus(status, null); } /** * Construct a stateful API return with data * * @param status Status {@ link Status} * @param data Return data * @return ApiResponse */ public static ApiResponse ofStatus(Status status, Object data) { return of(status.getCode(), status.getMessage(), data); } /** * Construct an API return with exception and data * * @param t abnormal * @param data Return data * @param <T> {@link BaseException} Subclass of * @return ApiResponse */ public static <T extends BaseException> ApiResponse ofException(T t, Object data) { return of(t.getCode(), t.getMessage(), data); } /** * Construct an API return with exception and data * * @param t abnormal * @param <T> {@link BaseException} Subclass of * @return ApiResponse */ public static <T extends BaseException> ApiResponse ofException(T t) { return ofException(t, null); } }
2.2 status code
You can expand yourself, just these two for the time being
@Getter public enum Status { /** * Operation succeeded */ OK(200, "Operation succeeded"), /** * Unknown exception */ UNKNOWN_ERROR(500, "Server error"); /** * Status code */ private Integer code; /** * content */ private String message; Status(Integer code, String message) { this.code = code; this.message = message; }
3. Exception related
3.1 user defined basic exception
Inherits the RuntimeException to serve the following custom exceptions
Students who are not clear about @ EqualsAndHashCode
You can see the explanation of this article
Use of @ EqualsAndHashCode(callSuper = false) of Lombok
@Data @EqualsAndHashCode(callSuper = true) public class BaseException extends RuntimeException { private Integer code; private String message; public BaseException(Status status) { super(status.getMessage()); this.code = status.getCode(); this.message = status.getMessage(); } public BaseException(Integer code, String message) { super(message); this.code = code; this.message = message; } }
3.2 Json format exception handling
Recommended for front and rear end separation
@Getter public class JsonException extends BaseException { public JsonException(Status status) { super(status); } public JsonException(Integer code, String message) { super(code, message); } }
3.3 template page exception handling
Used for thymeleaf page processing to return static pages
@Getter public class PageException extends BaseException { public PageException(Status status) { super(status); } public PageException(Integer code, String message) { super(code, message); } }
4. Core: unified exception handling
@ControllerAdvice+@ExceptionHandler are a perfect match
The former is added to the class to specify that the class is an exception handling class
The latter one is added to the method to specify what exception types are caught
ps: of course you can add an Exception class to handle all exceptions
4.2 handling json + page exceptions
@ControllerAdvice @Slf4j public class DemoExceptionHandler { private static final String DEFAULT_ERROR_VIEW = "error"; /** * Unified json exception handling * * @param exception JsonException * @return Unified return json format */ @ExceptionHandler(value = JsonException.class) @ResponseBody public ApiResponse jsonErrorHandler(JsonException exception) { log.error("[JsonException]:{}", exception.getMessage()); return ApiResponse.ofException(exception); } /** * Unified page exception handling * * @param exception PageException * @return Unified jump to exception page */ @ExceptionHandler(value = PageException.class) public ModelAndView pageErrorHandler(PageException exception) { log.error("[DemoPageException]:{}", exception.getMessage()); ModelAndView view = new ModelAndView(); view.addObject("code", exception.getCode()); view.addObject("message", exception.getMessage()); view.setViewName(DEFAULT_ERROR_VIEW); return view; } }
4.2 error template page error html
Don't put it in the wrong position
In the src/main/resources/templates directory
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head lang="en"> <meta charset="UTF-8"/> <title>Unified page exception handling</title> </head> <body> <h1>Unified page exception handling</h1> <div th:text="'Error code:'+${code}"></div> <div th:text="'Error message:'+${message}"></div> </body> </html>
5. controller test
@Controller @Slf4j public class BbController { @GetMapping("hello") @ResponseBody public ApiResponse hello() { // If the result is returned normally, we don't need to throw an exception. We can use ApiResponse directly return ApiResponse.ofStatus(Status.OK,"hello bb"); } @GetMapping("hello2") @ResponseBody public ApiResponse hello2() { // Directly return unknown error json format throw new JsonException(Status.UNKNOWN_ERROR); } @GetMapping("hello3") //No need to add @ ResponseBody public String hello3() { //The static page error is returned html throw new PageException(Status.UNKNOWN_ERROR); } }
It should be noted that if you return a template page, you need to introduce the thymeleaf dependency and do not need to add @ ResponseBody
5.1 access http://localhost:8080/hello
5.2 access http://localhost:8080/hello2
You can see the information printed on the console to facilitate our troubleshooting
2021-08-01 11:41:00.355 ERROR 3284 --- [nio-8080-exec-1] c.b.e.exception.DemoExceptionHandler : [JsonException]:Server error 2021-08-01 11:41:00.372 WARN 3284 --- [nio-8080-exec-1] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [BaseException(code=500, message=Server error)]
5.3 access http://localhost:8080/hello3
The information printed on the console is the same. There is no screenshot here
One last point
Unified exception handling can only handle exceptions at the Controller level
For the exception caused by the mapper service, either throw it to the controller layer
Or try catch and handle it yourself