preface

As mentioned in the previous article, parameter validation is often used in conjunction with a global exception interceptor so that the data structure returned is always consistent. Springboot default return structure:

{
 "timestamp": "The 2019-04-25 T13: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 expression"\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"
}
Copy the code

It is better for the front end (or app) to have the same structure of the return value for easy interpretation, whether it is normal or abnormal.

Define a BaseResult class that defines the data structure that returns the value

public class BaseResult { private int code; private String message; private Object data; // omit getter setter method, full argument constructor}Copy the code

Whatever the interface, the data structure is returned to the front end. For example, if the convention code is 0, it is successful. For other errors, specific error codes are defined. Message puts error messages, and data objects put corresponding data.

Define global exception handler GlobalExceptionHandlerAdvice

@RestControllerAdvice
public class GlobalExceptionHandlerAdvice {

}
Copy the code

RestControllerAdvice allows you to identify a class as an exception catching class.

Catch exceptions

Through the test parameters of abnormal, can know parameters have exception thrown when org. Springframework. Web. Bind. MethodArgumentNotValidException. We now catch the exception manually and return a BaseResult response.

@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);
}
Copy the code

ExceptionHandler is used to catch exception types. The exception thrown by the argument error is caught and the custom result is returned. Here error code for random fill, real development, it is recommended to define an error code enumeration class.

The effect is as follows:

The result is friendlier and easier to process on the front end.

Exception flows handle business logic

Using exceptions to handle business logic makes code much smoother to write. For example, a method that deletes user data returns void (no return value), but if the user ID passed in does not exist, it should return a result that does not exist. However, using the exception flow to handle the business logic becomes very simple. We simply throw a custom exception, catch it on the exception catcher, 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() {
 returnthis; } // omit the getter setter method}Copy the code

Here we define a runtime exception that overrides the fillInStackTrace method by leaving the exception stack intact. Because we use this exception to handle business logic, which we throw manually, there is no need to save the exception stack, which improves performance.

Add a WebException exception catch to the exception catcher

@ExceptionHandler(WebException.class)
public BaseResult handleWebException(WebException e) {
 return new BaseResult(e.getCode(), e.getErrorMsg(), null);
}
Copy the code

Simulate a piece of business logic and throw a WebException

In the previous UserController class, modify the deleteUser method 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);
}

Copy the code

Here we define an interface for a DELETE request that accepts a userId parameter and returns that the user does not exist if the userId is 0. The test results are as follows:

When userId is 0, a message is displayed indicating that the user does not exist

When userId is 1, a message is displayed indicating success.

conclusion

Global exception catching is implemented and the exception flow handling business logic is introduced. This is just a small demo, and there’s a lot of room for improvement. For example, I didn’t define an error code enumeration class. In the premise of defining the error code enumeration class, modify the construction of BaseResult mode, can use static factory mode to construct. I won’t go into the discussion here.