Bean Validation 2.0 (JSR 380) 是 Java EE 和 Java SE 中用于 JavaBean 验证的 Java API 规范。
public class SaleOrderAfterSaleInitiateCommand {
@NotNull(message = "销售订单ID不能为空")
private Long saleOrderId;
}
public static class Product {
@NotBlank(message = "产品名称不能为空")
private final String productName;
}
public static class Product {
@NotEmpty(message = "产品图片不能为空")
private final List<String> productImage;
}
public class SaleOrderAfterSaleInitiateCommand {
@Digits(integer = 9, fraction = 2, message = "退款金额整数位最多9位,小数位最多2位")
private BigDecimal refundAmount;
}
public class Product {
@Max(value = 100, message = "产品箱规不能大于 100")
@Min(value = 1, message = "产品箱规不能小于 1")
private Integer boxGauge;
}
public class Product {
@Positive(message = "产品箱规必须是正整数")
private Integer boxGauge;
}
public class Product {
@PositiveOrZero(message = "产品箱规必须是正整数或0")
private Integer boxGauge;
}
public class Product {
@Negative(message = "产品箱规必须是负数")
private Integer boxGauge;
}
public class Product {
@NegativeOrZero(message = "产品箱规必须是负数或0")
private Integer boxGauge;
}
public class SaleOrderAfterSaleInitiateCommand {
@DecimalMax(value = "10000000.00", message = "退款金额需小于 10000000.00")
@DecimalMin(value = "0.01", message = "退款金额需大于 0.00")
private BigDecimal refundAmount;
}
public class Manufacturer {
@Length(max = 100, message = "产品生产商名称长度不能大于100")
private String name;
@Length(max = 500, message = "产品生产商地址长度不能大于500")
private String address;
}
public static class User {
@Past(message = "用户生日必须是过去的一个日期")
private final Date birthday;
}
public static class User {
@PastOrPresent(message = "用户生日必须是过去的一个日期与当前日期")
private final Date birthday;
}
public static class User {
@Future(message = "用户生日必须是未来的一个日期")
private final Date birthday;
}
public static class User {
@FutureOrPresent(message = "用户生日必须是未来的一个日期与当前日期")
private final Date birthday;
}
public static class Product {
@Size(max = 5, message = "产品图片数量不能大于5")
private final List<String> productImage;
}
public static class Product {
private final List<@Length(max = 5, message = "产品图片长度不能大于5个字符") String> productImage;
}
public static class Product {
private final List<@Valid Manufacturer> manufacturerList;
}
public class Manufacturer {
@NotBlank(message = "产品生产商名称不能为空!")
@Length(max = 100, message = "产品生产商名称长度不能大于100")
private final String name;
@NotBlank(message = "产品生产商地址不能为空!")
@Length(max = 500, message = "产品生产商地址长度不能大于500")
private final String address;
}
public class User {
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "用户手机号不正确")
private String phoneNumber;
}
public static class User {
@Email(message = "用户邮箱格式不正确")
private String email;
}
@Documented
@Constraint(validatedBy = PasswordValidator.class)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface Password {
String message() default "密码长度为8~32个字符,必须包含大写英文字母、小写英文字母、数字、特殊符号每种至少各一个";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
public class PasswordValidator implements ConstraintValidator<Password, String> {
@Override
public boolean isValid(String password, ConstraintValidatorContext constraintValidatorContext) {
if (StringUtils.isBlank(password)) {
return false;
}
// 密码长度为8~32个字符,必须包含大写英文字母、小写英文字母、数字、特殊符号每种至少各一个
return password.matches("^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[$@!%*?&^+.~])[A-Za-z0-9$@!%*?&^+.~]{8,32}$");
}
}
@Documented
@NotBlank(message = "密码不能为空")
@Constraint(validatedBy = PasswordValidator.class)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface Password {
String message() default "密码长度为8~32个字符,必须包含大写英文字母、小写英文字母、数字、特殊符号每种至少各一个";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
public class PasswordValidator implements ConstraintValidator<Password, String> {
@Override
public boolean isValid(String password, ConstraintValidatorContext constraintValidatorContext) {
// 密码长度为8~32个字符,必须包含大写英文字母、小写英文字母、数字、特殊符号每种至少各一个
return password.matches("^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[$@!%*?&^+.~])[A-Za-z0-9$@!%*?&^+.~]{8,32}$");
}
}
public class Validator<T> {
public void validate(T t) {
Set<ConstraintViolation<T>> validate =
Validation.buildDefaultValidatorFactory().getValidator().validate(t);
// 所有参数包含在内
String validateString = validate.stream().map(v -> String.format("%s:%s", v.getPropertyPath(), v.getMessage())).collect(Collectors.joining(";"));
if (StringUtils.isNotBlank(validateString)) {
throw new IllegalArgumentException(validateString);
}
}
}
public void addProduct(Product product) {
Validator<Product> validator = new Validator<>();
validator.validate(product);
}
public class Product {
@NotBlank(message = "不能为空")
private String productName;
@NotBlank(message = "不能为空")
private String barCode;
}
Spring Bean Validation 是 Spring 提供了对 Java Bean Validation API 的支持。
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-validationartifactId>
dependency>
dependencies>
@GetMapping("/v1/info/{productId}")
public BaseResponseDTO<Product> queryProductInfo(@PathVariable("productId") @NotNull(message = "产品ID不能为空") Long productId) {
return success(name);
}
@GetMapping("/v1/info")
public BaseResponseDTO<Product> queryProductInfo(@RequestParam("productId") @NotNull(message = "产品ID不能为空") Long productId) {
return success(name);
}
@PostMapping("/add")
public BaseResponseDTO<ReplenishmentPlanAddDTO> addReplenishmentPlan(@RequestBody @Validated ReplenishmentPlanAddCommand replenishmentPlanAddCommand){
ReplenishmentPlanAddDTO replenishmentPlanAddDTO = replenishmentPlanCommandApplicationService.addReplenishmentPlan(replenishmentPlanAddCommand);
return successWithData(replenishmentPlanAddDTO);
}
@RestControllerAdvice
public class GlobalExceptionHandler {
private static final LogUtil LOG_UTIL = LogUtil.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(MissingServletRequestParameterException.class)
public BaseResponse<Object> handleError(MissingServletRequestParameterException e) {
LOG_UTIL.warn("Missing Request Parameter", e);
String message = String.format("Missing Request Parameter: %s", e.getParameterName());
return new BaseResponse<>(ResultCode.PARAM_MISS.getCode(),message,null);
}
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
public BaseResponse<Object> handleError(MethodArgumentTypeMismatchException e) {
LOG_UTIL.warn("Method Argument Type Mismatch", e);
String message = String.format("Method Argument Type Mismatch: %s", e.getName());
return new BaseResponse<>(ResultCode.PARAM_TYPE_ERROR.getCode(),message,null);
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public BaseResponse<Object> handleError(MethodArgumentNotValidException e) {
LOG_UTIL.warn("Method Argument Not Valid", e);
BindingResult result = e.getBindingResult();
FieldError error = result.getFieldError();
assert error != null;
String message = String.format("%s:%s", error.getField(), error.getDefaultMessage());
return new BaseResponse<>(ResultCode.PARAM_VALID_ERROR.getCode(),message,null);
}
@ExceptionHandler(BindException.class)
public BaseResponse<Object> handleError(BindException e) {
LOG_UTIL.warn("Bind Exception", e);
FieldError error = e.getFieldError();
assert error != null;
String message = String.format("%s:%s", error.getField(), error.getDefaultMessage());
return new BaseResponse<>(ResultCode.PARAM_BIND_ERROR.getCode(),message,null);
}
@ExceptionHandler(ConstraintViolationException.class)
public BaseResponse<Object> handleError(ConstraintViolationException e) {
LOG_UTIL.warn("Constraint Violation", e);
Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
ConstraintViolation<?> violation = violations.iterator().next();
String path = ((PathImpl) violation.getPropertyPath()).getLeafNode().getName();
String message = String.format("%s:%s", path, violation.getMessage());
return new BaseResponse<>(ResultCode.PARAM_VALID_ERROR.getCode(),message,null);
}
}