java spring validation 自动、手动校验
作者:木辰風
HibernateValidator简化了Java开发中的参数校验过程,提供自动和手动两种校验方式,通过引入相关依赖并使用@Validated注解,可以实现自动校验,手动校验则需要使用ValidatorUtils类,此方法有效减少代码重复,提高开发效率
参数校验是一个常见的问题,比如字段非空,字段长度限制,邮箱格式、手机格式验证等等。
避免校验规则,写一大串步骤,繁琐重复。
Hibernate Validator为此提供了一套比较完善、便捷的验证实现方式。
一、自动校验
第一步,导入依赖
项目已经引入spring-boot-starter-web包里面有hibernate-validator包,不需要引用hibernate validator依赖。
项目还未引入spring-boot-starter-web包可以引入以下依赖:
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>4.3.1.Final</version> </dependency>
第二步,统一异常处理
ValidateExceptionController
import com.central.common.model.Result; import lombok.extern.slf4j.Slf4j; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.validation.BindingResult; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import java.util.HashMap; import java.util.Map; /** * 统一异常处理 */ @Slf4j @RestControllerAdvice @Order(Ordered.HIGHEST_PRECEDENCE) public class ValidateExceptionController { //如果能精确匹配到该异常就会执行这个方法,后续执行下面的方法 @ExceptionHandler(value = MethodArgumentNotValidException.class) public Result handelValidateException(MethodArgumentNotValidException e) { log.error("数据校验出现问题:{},异常类型:{}", e.getMessage(), e.getClass()); Map<String, String> map = new HashMap<>(); //1.获取校验错误结果 BindingResult result = e.getBindingResult(); result.getFieldErrors().forEach(fieldError -> { //获取每个错误的属性名字 String field = fieldError.getField(); //获取每个错误提示信息 String defaultMessage = fieldError.getDefaultMessage(); map.put(field, defaultMessage); }); return Result.failed(map, "数据校验错误"); } }
第三步,定义接口接收实体DTO
定义校验规则,可以参考
关于Java Validation (验证注解) 见文末补充介绍。
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import javax.validation.Valid; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import java.io.Serializable; import java.util.List; @Data @ApiModel("售后申请接口-接收实体DTO") public class AfterServiceApplyDTO implements Serializable { @NotBlank(message = "订单编号不为空") @ApiModelProperty(value = "订单编号", required = true) private String grandsonOrderCode; @NotNull(message = "申请售后类型不为空") @ApiModelProperty(value = "申请售后类型: 10:退货 20:换货 30:维修", required = true) private Integer customerExpect; @NotBlank(message = "产品问题描述不为空") @ApiModelProperty(value = "产品问题描述", required = true) private String questionDesc; @ApiModelProperty("问题描述图片链接地址,多个图片以“,”分隔") private String questionPic; @Valid @NotNull(message = "客户信息实体不为空") @ApiModelProperty("客户信息实体") private AfterSaleCustomerDTO asCustomerDto; @Valid @Size(min = 1, max = 1, message = "只能申请1个商品") @NotNull(message = "申请单明细列表不为空") @ApiModelProperty("申请单明细列表") private List<AfterSaleDetailDTO> asDetailDtos; }
第四步,在Contoller接口中增加参数注解@Validated
表示只校验当前参数
@Api(tags = "【售后】订单售后API接口") @RestController public class AfterServiceApiController { /** * 售后申请接口 * @param afterServiceApplyDTO 售后申请参数 * @return 操作结果 */ @ApiOperation("售后申请接口") @PostMapping("/afterService/api/apply") public Result apply(@Validated @RequestBody AfterServiceApplyDTO afterServiceApplyDTO) { // todo return null; } }
第五步,测试结果
Postman发送错误数据触发验证测试
二、手动校验
第一步,校验工具类
ValidatorUtils
import com.central.business.afterService.dto.AfterServiceApplyDTO; import com.central.common.model.Result; import javax.validation.ConstraintViolation; import javax.validation.Validation; import javax.validation.Validator; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; /** * 校验工具类 */ public class ValidatorUtils { private static final Validator validator; // 第一种方式创建Validator static { // 普通模式(默认是这个模式) // 普通模式(会校验完所有的属性,然后返回所有的验证失败信息) validator = Validation.buildDefaultValidatorFactory().getValidator(); } //第二种方式创建Validator // static { // // 1.普通模式(默认是这个模式) // // 普通模式(会校验完所有的属性,然后返回所有的验证失败信息) // // .failFast(false) // // 或 .addProperty("hibernate.validator.fail_fast", "false") // // // 2.快速失败返回模式 // // 快速失败返回模式(只要有一个验证失败,则返回) // // .addProperty("hibernate.validator.fail_fast", "true") // // 或 .failFast(true) // ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class) // .configure() // .failFast(true) .addProperty("hibernate.validator.fail_fast", "true") // .buildValidatorFactory(); // validator = validatorFactory.getValidator(); // } /** * 校验对象,返回校验失败List信息 * * @param object 对象 * @param groups 组 * @return 校验失败List信息 */ public static List<String> validateEntity(Object object, Class<?>... groups) { List<String> list = new ArrayList<>(); Set<ConstraintViolation<Object>> validate = validator.validate(object, groups); for (ConstraintViolation<Object> violation : validate) { list.add(violation.getMessage()); } return list; } /** * 校验对象,返回校验失败Map信息 * * @param object 对象 * @param groups 组 * @return 校验失败Map信息,key为属性(字段名),value为校验失败信息 */ public static Map<String, String> validateEntityProperty(Object object, Class<?>... groups) { Map<String, String> map = new HashMap<>(); Set<ConstraintViolation<Object>> validate = validator.validate(object, groups); for (ConstraintViolation<Object> violation : validate) { map.put(violation.getPropertyPath().toString(), violation.getMessage()); } return map; } /** * 校验对象,返回校验失败Result信息 * * @param object 对象 * @param groups 组 * @return 校验失败Result,校验失败返回错误信息,成功返回成功信息 */ public static Result<Map<String, String>> validateEntityResult(Object object, Class<?>... groups) { Map<String, String> map = new HashMap<>(); Set<ConstraintViolation<Object>> validate = validator.validate(object, groups); for (ConstraintViolation<Object> violation : validate) { map.put(violation.getPropertyPath().toString(), violation.getMessage()); } if (map.size() > 0) { return Result.failed(map, "数据校验错误!"); } return Result.succeed("数据校验成功!"); } public static void main(String[] args) { AfterServiceApplyDTO afterServiceApplyDTO = new AfterServiceApplyDTO(); System.out.println(validateEntityResult(afterServiceApplyDTO)); } }
第二步,测试结果
Result(datas={questionDesc=产品问题描述不为空, grandsonOrderCode=供应链三级订单编号不为空, reason=售后原因不为空, asCustomerDto=客户信息实体不为空, asDetailDtos=申请单明细列表不为空, businessPlatformCode=业务商城售后申请单号不为空, customerExpect=申请售后类型不为空}, resp_code=1, resp_msg=数据校验错误!)
补充:Java Validation (验证注解)
验证注解 | 验证的数据类型 | 说明 |
---|---|---|
@AssertFalse | Boolean,boolean | 验证注解的元素值是false |
@AssertTrue | Boolean,boolean | 验证注解的元素值是true |
@NotNull | 任意类型 | 验证注解的元素值不是null |
@Null | 任意类型 | 验证注解的元素值是null |
@Min(value=值) | BigDecimal,BigInteger, byte,short, int, long,等任何Number或CharSequence(存储的是数字)子类型 | 验证注解的元素值大于等于@Min指定的value值 |
@Max(value=值) | 和@Min要求一样 | 验证注解的元素值小于等于@Max指定的value值 |
@DecimalMin(value=值) | 和@Min要求一样 | 验证注解的元素值大于等于@ DecimalMin指定的value值 |
@DecimalMax(value=值) | 和@Min要求一样 | 验证注解的元素值小于等于@ DecimalMax指定的value值 |
@Digits(integer=整数位数, fraction=小数位数) | 和@Min要求一样 | 验证注解的元素值的整数位数和小数位数上限 |
@Size(min=下限, max=上限) | 字符串、Collection、Map、数组等 | 验证注解的元素值的在min和max(包含)指定区间之内,如字符长度、集合大小 |
@Past | java.util.Date,java.util.Calendar;Joda Time类库的日期类型 | 验证注解的元素值(日期类型)比当前时间早 |
@Future | 与@Past要求一样 | 验证注解的元素值(日期类型)比当前时间晚 |
@NotBlank | CharSequence子类型 | 验证注解的元素值不为空(不为null、去除首位空格后长度为0),不同于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的首位空格 |
@Length(min=下限, max=上限) | CharSequence子类型 | 验证注解的元素值长度在min和max区间内 |
@NotEmpty | CharSequence子类型、Collection、Map、数组 | 验证注解的元素值不为null且不为空(字符串长度不为0、集合大小不为0) |
@Range(min=最小值, max=最大值) | BigDecimal,BigInteger,CharSequence, byte, short, int, long等原子类型和包装类型 | 验证注解的元素值在最小值和最大值之间 |
@Email(regexp=正则表达式,flag=标志的模式) | CharSequence子类型(如String) | 验证注解的元素值是Email,也可以通过regexp和flag指定自定义的email格式 |
@Pattern(regexp=正则表达式,flag=标志的模式) | String,任何CharSequence的子类型 | 验证注解的元素值与指定的正则表达式匹配 |
@Valid | 任何非原子类型 | 指定递归验证关联的对象如用户对象中有个地址对象属性,如果想在验证用户对象时一起验证地址对象的话,在地址对象上加@Valid注解即可级联验证 |
到此这篇关于java spring validation 自动、手动校验的文章就介绍到这了,更多相关java spring validation校验内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!