Preface
As mentioned in the previous article, parameter validation often needs to be used in conjunction with global exception interceptors, so that the data structure returned is always consistent. The default return structure for parameter exception springboot:
{ "timestamp": "2019-04-25T13:09:02.196+0000", "status": 400, "error": "Bad Request", "errors": [ { "codes": [ "Pattern.param.birthday", "Pattern.birthday", "Pattern.java.lang.String", "Pattern" ], "arguments": [ { "codes": [ "param.birthday", "birthday" ], "arguments": null, "defaultMessage": "birthday", "code": "birthday" }, [], { "defaultMessage": "\d{4}-\d{2}-\d{2}", "codes": [ "\d{4}-\d{2}-\d{2}" ], "arguments": null } ], "defaultMessage": "Need to match regular expressions"\d{4}-\d{2}-\d{2}"", "objectName": "param", "field": "birthday", "rejectedValue": "apple", "bindingFailure": false, "code": "Pattern" } ], "message": "Validation failed for object='param'. Error count: 1", "path": "/validate/notblank" }
Whether normal or abnormal, for the front end (or app), the structure of the best return value is the same, so that it can be easily explained.
Define a BaseResult class that defines the data structure of the return value
public class BaseResult { private int code; private String message; private Object data; // Omitting getter setter method and constructing method with full parameters }
Regardless of the interface, this data structure is used to return to the front end. For example, if the agreement code is 0, it is successful. Other errors define specific error codes. message puts error messages and data objects put corresponding data.
Define Global Exception Handler Advice
@RestControllerAdvice public class GlobalExceptionHandlerAdvice { }
Using RestController Advice, you can identify a class as an exception capture class.
Catching exceptions
Through the test of parameter exceptions, it can be known that when the parameters are exceptional, an org. spring framework. web. bind. MethodArgumentNotValidException will be thrown. We now capture this exception manually and return a response in BaseResult format.
@ExceptionHandler(MethodArgumentNotValidException.class) public BaseResult handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { BindingResult result = e.getBindingResult(); FieldError fieldError = result.getFieldError(); String defaultMessage = fieldError.getDefaultMessage(); return new BaseResult(11000, defaultMessage, null); }
Exception Handler is used to capture exception types. Here, exceptions thrown by parameter errors are captured, and custom results are returned. The error code here is to fill in casually, real development, it is recommended to define an error code enumeration class.
The results are as follows:
The results returned are more friendly, and the front-end processing is also convenient.
Exception Flow Processing Business Logic
Using exceptions to handle business logic can make the code write more smoothly. For example, a method of deleting user data, the return value is void (no return value), but when the incoming user id does not exist, it should return a result that the user does not exist, which seems powerless for the method of void return value. However, using exception flows to handle the business logic can be very simple. We throw a custom exception directly, catch the exception on the exception trap, and return the result to the front end.
Define a WebException
public class WebException extends RuntimeException { private int code; private String errorMsg; public WebException(int code, String errorMsg) { super(errorMsg); this.code = code; this.errorMsg = errorMsg; } @Override public synchronized Throwable fillInStackTrace() { return this; } // Omitting getter setter method }
A runtime exception is defined and the fillInStackTrace method is rewritten, which does not retain the exception information stack. Because we use this exception to handle business logic, we throw it manually, so we don't need to save the exception information stack, which will improve performance.
Adding WebException exception capture to exception capture
@ExceptionHandler(WebException.class) public BaseResult handleWebException(WebException e) { return new BaseResult(e.getCode(), e.getErrorMsg(), null); }
Simulate a piece of business logic and throw WebException
In the previous UserController class, modify the deleteUser method written before, as follows:
@DeleteMapping(value = "/{userId}") public Object deleteUser(@PathVariable(value = "userId") Integer userId) { if (userId == 0) { throw new WebException(-1, "user does not exist"); } return new BaseResult(1, "Success", null); }
This defines an interface for delete requests, receives a userId parameter, and returns that the user does not exist if userId equals 0. The test results are as follows:
When userId is 0, the user is prompted not to exist
Success is prompted when userId is 1.
summary
This paper implements global exception capture and introduces the business logic of exception flow processing. This is just a small demo, there are still many areas to be improved. For example, I did not define an error code enumeration class. On the premise of defining the enumeration class of error codes, we can modify the pattern of constructing BaseResult and adopt the static factory pattern to construct it. There is no discussion here.