一、SpringBoot之参数校验
1、SpringBoot提供的参数校验注解
除了@NotEmpty和@NotBlank将 null 值认为是非法的之外,其它注解如@Size, @Max, @Min等都将 null 认为是有效的,如果不允许 null 值,则需要额外添加@NotNull注解。
| 注解 | 说明 |
|---|---|
**@NotEmpty** |
字符串、集合、Map、数组等不能为 null 或空 |
**@NotBlank** |
字符串不能为 null,且至少包含一个非空字符 |
**@NotNull** |
任意类型 不能为 null |
**@Size** |
字符串、集合、Map、数组等元素的个数必须在指定的 min 和 max 范围内 |
**@Email** |
字符串是有效的邮箱 |
**@Digits** |
字符串、整数、浮点数是一个数字,参数 integer 表示整数位的数字个数,fraction 表示小数位的数字个数 |
**@CreditCardNumber** |
字符串是有效的信用卡数字,不校验信用卡本身的有效性 |
**@AssertTrue** |
布尔类型必须是 true,null 值被认为是有效的 |
**@Max** |
整数、浮点数必须小于等于指定的最大值 |
**@Min** |
整数、浮点数必须大于等于指定的最小值 |
**@Range** |
字符串、数字必须在指定的 min 和 max 范围内 |
**@Pattern** |
字符串必须匹配指定的正则表达式 |
@Datapublic class Employee implements Serializable {private static final long serialVersionUID = -8224860450904540019L;@NotEmpty(message = "名字不能为空")@UTF8Size(max = 16, message = "name should be short than 128")private String name;private String email;@NotBlank(message = "city is required")@Size(max = 128, message = "city should be short than 128")private String city;@CreditCardNumber(message = "invalid credit card number")private String ccNumber;@Pattern(regexp = "^(0[1-9]|1[0-2])([\\\\/])([1-9][0-9])$", message = "required format MM/YY")private String ccExpiration;@Digits(integer = 3, fraction = 0, message = "invalid CVV")private String ccCVV;}
2、自定义校验注解
@Size并不能支持中文字符,可以自定义如下然后在需要校验字符(中英文)的字段上使用@UTF8Size即可。
@Documented@Constraint(validatedBy = Utf8SizeValidator.class)@Target({METHOD, FIELD})@Retention(RetentionPolicy.RUNTIME)public @interface UTF8Size {String message() default "{javax.validation.constraints.Size.message}";int min() default 0;int max() default Integer.MAX_VALUE;Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};}
public class Utf8SizeValidator implements ConstraintValidator<UTF8Size, String> {private int maxCharSize;@Overridepublic void initialize(UTF8Size constraintAnnotation) {this.maxCharSize = constraintAnnotation.max();}@Overridepublic boolean isValid(String value, ConstraintValidatorContext context) {if (Objects.isNull(value)) {return true;}return value.getBytes(Charset.forName("GB18030")).length <= maxCharSize;}}
二、字段校验的使用
使用 Bean Validation API 的@Valid注解,或者 Spring Context 提供的@Validated注解启用对 Bean 的校验。主要区别是,@Validated是@Valid的变体,支持分组校验(validation groups)。
A.这种写法会将异常信息抛给全局异常处理
@PostMapping("/emp")public String addEmploy(@RequestBody @Valid Employee employee) {log.info("employee to create: {}", employee);String employeeId = UUID.randomUUID().toString();return employeeId;}
B.如果需要捕获参数校验的异常结果,写法如下
:::danger 这种写法不会将异常处理的结果返回给全局异常处理 :::
@PostMapping()UserInfo addUser(@RequestBody @Valid UserInfo userInfo, BindingResult bindingResult) {userService.addUser(userInfo);return userInfo;}
三、字段校验的全局异常处理
package org.hand.train.springboot.springboot.exception;import lombok.extern.slf4j.Slf4j;import org.springframework.http.HttpStatus;import org.springframework.validation.BindingResult;import org.springframework.validation.FieldError;import org.springframework.web.bind.MethodArgumentNotValidException;import org.springframework.web.bind.annotation.ControllerAdvice;import org.springframework.web.bind.annotation.ExceptionHandler;import org.springframework.web.bind.annotation.ResponseBody;import org.springframework.web.bind.annotation.ResponseStatus;import java.util.stream.Collectors;/*** GlobalExceptionHandler* encoding:UTF-8** @author Fcant* @date 10:02 2019/12/4*/@Slf4j@ControllerAdvicepublic class GlobalExceptionHandler {@ExceptionHandler(MethodArgumentNotValidException.class)@ResponseBody@ResponseStatus(HttpStatus.BAD_REQUEST)public String handleMethodArgumentNotValidException(MethodArgumentNotValidException exception) {log.error("method argument not valid: {}", exception.getMessage());String errorMessage = "field error";BindingResult bindingResult = exception.getBindingResult();if (bindingResult.hasErrors()) {errorMessage = bindingResult.getFieldErrors().stream().map(FieldError::getDefaultMessage).collect(Collectors.joining(" | "));}return errorMessage;}}
四、分组校验
A.组接口
public class GroupVaildDTO {public interface SaveGroup extends Default {}public interface UpdateGroup extends Default {}}
B.校验的JavaBean属性
public class GroupsValidForm {@Null(message = "id必须为null", groups = {GroupVaildDTO.SaveGroup.class})@NotNull(message = "id不能为null", groups = {GroupVaildDTO.UpdateGroup.class})private Integer id;@NotBlank(message = "用户名不能为空")private String userName;@Overridepublic String toString() {final StringBuffer sb = new StringBuffer("GroupsValidForm{");sb.append("id=").append(id);sb.append(", userName='").append(userName).append('\'');sb.append('}');return sb.toString();}}
C.Controller层分组校验的使用
相同字段在不同场景需要不同的验证策略
@PostMapping(value = "update")public ServerResponse update(@RequestBody @Validated(value = GroupVaildDTO.UpdateGroup.class)GroupsValidForm groupsValidForm, BindingResult results) {if (results.hasErrors()) {return ServerResponse.createByErrorMessage(results.getFieldError().getDefaultMessage());}return ServerResponse.createBySuccess();}
