preface
We all know that when we write a controller, we need to check the request parameters at the back end, so we might write something like this
public String add(UserVO userVO) {
if(userVO.getAge() == null) {return "Age cannot be empty.";
}
if(userVO.getAge() > 120) {return "No more than 120.";
}
if(userVO.getName().isEmpty()){
return "User name cannot be empty";
}
// omit a bunch of parameter verification...
return "OK";
}
Copy the code
Business code has not started to write, optical parameter verification to write a bunch of judgment. There’s nothing wrong with this, but it feels like: unelegant, unprofessional, unreadable code that looks like freshly written code
SpringBoot provides a validation solution that integrates the validation parameters with spring-boot-starter-validation
Integration of use
Versions prior to SpringBootv2.3 only needed to import web dependencies, which included the validation validation package. After that, the SpringBoot version became independent and needed to import its own dependencies
<! -- Parameter verification -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
Copy the code
There are many built-in validation annotations, listed as follows:
annotations | Check the function |
---|---|
@AssertFalse | Must be false |
@AssertTrue | It must be true |
@DecimalMax | Less than or equal to a given value |
@DecimalMin | Greater than or equal to a given value |
@Digits | Maximum number of integer and decimal digits can be set |
Verify that the Email format is correct | |
@Future | It has to be in the future |
@FutureOrPresent | Present or future time |
@Max | The maximum |
@Min | The minimum value |
@Negative | Negative numbers (excluding 0) |
@NegativeOrZero | Negative or zero |
@NotBlank | Is not null and contains at least one non-whitespace character |
@NotEmpty | Not null and not null |
@NotNull | Not null |
@Null | null |
@Past | It has to be in the past |
@PastOrPresent | It has to be the past, including the present |
@Pattern | The regular expression must be satisfied |
@PositiveOrZero | Positive or zero |
@Size | Validates the number of elements in the container |
Single parameter check
So it’s very simple to use it so you just put @Validated on the controller that you want to validate and you put @ null, @NotEmpty or something like that on the parameter that you want to validate,
@Validated
@GetMapping("/home")
public class ProductController {
public Result index(@NotBlank String name, @Email @NotBlank String email) {
returnResultResponse.success(); }}Copy the code
Object parameter verification
So it’s just a matter of annotating @validated, @validated, @NOtnull, @NOtempty, etc., on the properties of the object parameters that you want to validate,
@PostMapping("/user")
public Result index1(@Validated @RequestBody UserParams userParams) {
log.info("info test######");
log.error("error test #####");
return ResultResponse.success(userParams);
}
Copy the code
@Data
public class UserParams {
@NotBlank
private String username;
private int age;
@NotBlank
private String addr;
@Email
private String email;
}
Copy the code
Abnormal information processing of parameter verification
We conducted above parameter calibration, the default when parameter calibration after didn’t pass through the abnormal way to throw an error information is thrown when checking MethodArgumentNotValidException also includes many other abnormal information, then we can use the global capture information to deal with these parameters calibration anomaly
The global exception handling class simply needs to annotate @RestControllerAdvice on the class and specify which exception to handle with the @ExceptionHandler annotation on the method that handles the corresponding exception
package cn.soboys.core;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import cn.soboys.core.authentication.AuthenticationException;
import cn.soboys.core.ret.Result;
import cn.soboys.core.ret.ResultCode;
import cn.soboys.core.ret.ResultResponse;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Path;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/ * * *@author kenx
* @version 1.0
* @date2021/6/17 20:19 * Unified handling of global exceptions */
@RestControllerAdvice
public class GlobalExceptionHandler {
/** * Handle the exception thrown when the json request body calls the interface object parameter verification failure */
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result jsonParamsException(MethodArgumentNotValidException e) {
BindingResult bindingResult = e.getBindingResult();
List errorList = CollectionUtil.newArrayList();
for (FieldError fieldError : bindingResult.getFieldErrors()) {
String msg = String.format("% s % s.", fieldError.getField(), fieldError.getDefaultMessage());
errorList.add(msg);
}
return ResultResponse.failure(ResultCode.PARAMS_IS_INVALID, errorList);
}
/** * Handle the exception thrown by a single parameter validation failure */
@ExceptionHandler(ConstraintViolationException.class)
public Result ParamsException(ConstraintViolationException e) { List errorList = CollectionUtil.newArrayList(); Set<ConstraintViolation<? >> violations = e.getConstraintViolations();for(ConstraintViolation<? > violation : violations) { StringBuilder message =new StringBuilder();
Path path = violation.getPropertyPath();
String[] pathArr = StrUtil.splitToArray(path.toString(), ".");
String msg = message.append(pathArr[1]).append(violation.getMessage()).toString();
errorList.add(msg);
}
return ResultResponse.failure(ResultCode.PARAMS_IS_INVALID, errorList);
}
/ * * *@param e
* @returnHandle the exception */ thrown when the form data method fails to call the interface object parameter verification
@ExceptionHandler(BindException.class)
public Result formDaraParamsException(BindException e) {
List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
List<String> collect = fieldErrors.stream()
.map(o -> o.getField() + o.getDefaultMessage())
.collect(Collectors.toList());
return ResultResponse.failure(ResultCode.PARAMS_IS_INVALID, collect);
}
/** * The request method is not allowed to exception */
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public Result httpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
return ResultResponse.failure(ResultCode.METHOD_NOT_ALLOWED);
}
/ * * *@param e
* @returnContent-type /Accept * application/json * application/ x-w-form-urlencoded */
@ExceptionHandler(HttpMediaTypeNotSupportedException.class)
public Result httpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e) {
return ResultResponse.failure(ResultCode.BAD_REQUEST);
}
/** * The handlerMapping interface does not run abnormally **@param e
* @return* /
@ExceptionHandler(NoHandlerFoundException.class)
public Result noHandlerFoundException(NoHandlerFoundException e) {
return ResultResponse.failure(ResultCode.NOT_FOUND, e.getMessage());
}
/** * Authentication exception *@param e
* @return* /
@ExceptionHandler(AuthenticationException.class)
public Result UnNoException(AuthenticationException e) {
return ResultResponse.failure(ResultCode.UNAUTHORIZED,e.getMessage());
}
/ * * * *@paramE Unknown exception capture *@return* /
@ExceptionHandler(Exception.class)
public Result UnNoException(Exception e) {
returnResultResponse.failure(ResultCode.INTERNAL_SERVER_ERROR, e.getMessage()); }}Copy the code
For details about global exception handling, please refer to my article on elegant global exception handling in SpringBoot. Here I return a custom response body API. Please refer to my article on non-invasive Implementation RESTful API interface uniform JSON format
Of course, there are other ways to handle validation exceptions, which will help us to inject error messages into the BindingResult object if the parameter does not pass the validation, and into the corresponding Controller method, which only works if the parameter is @requestBody or @requestParam
public String add1(@Validated UserVO userVO, BindingResult result) {
List<FieldError> fieldErrors = result.getFieldErrors();
if(! fieldErrors.isEmpty()){return fieldErrors.get(0).getDefaultMessage();
}
return "OK";
}
Copy the code
Of course, I recommend the first way to uniformly handle verification exceptions through global exception handling
It would still be cumbersome to use if you wrote the BindingResult information in each Controller method. The code is redundant
When we have the @ “validated” annotation and no BindingResult, SpringBoot will throw an exception. As a result, you can write a global exception handling class to handle this validation exception uniformly, eliminating the need to repeatedly organize the exception information.
Pay attention to the public account ape life to get more dry goods to share