集成验证框架步骤
1、在pom.xml引入相关jar包
2、在待验证的实体里添加相应的注解
3、在controller中添加相应的注解
4、做参数校验工具类,完成service层的参数校验
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
有的版本在spring-boot-starter-web中集成了验证框架,就不用单独引入验证框架了。
新增请求中用户名不能为空,而修改请求用户名可以为空。针对同一个字段在不同请求中有不同的校验规则。需要定义多个接口
public interface InsertVaildationGroup {
}
public interface UpdateVaildationGroup {
}
@Data
public class UserDTO implements Serializable{
/**
*
*/
private static final long serialVersionUID = 5144803032215944382L;
@NotBlank(message = "用户名不能为空",groups = {InsertVaildationGroup.class})
private String username;
@Length(min = 6,max = 18,message = "密码长度需在6-18位之间")
private String password;
@NotEmpty(message = "邮箱不能为空!",groups = {InsertVaildationGroup.class})
@Email(message = "邮箱格式错误")
private String email;
@NotNull(message = "年龄不能为空",groups = {InsertVaildationGroup.class})
@Max(value = 60,message = "年龄不能大于60")
@Min(value = 18,message = "年龄不能小于18岁")
private Integer age;
@NotNull(message = "版本号不能为空",groups = {UpdateVaildationGroup.class})
private String phone;
/**
* 版本号
*/
private Long version;
}
通过groups属性来区分,在不同请求下的是否需要校验。如果没用groups ,那么如果值为空就不会校验,值不为空就会校验
@Validated
public class UserController {
@Autowired
private UserService userService;
@PostMapping()
public ResponseResult save(@Validated(value = {InsertVaildationGroup.class}) @RequestBody UserDTO userDTO) {
if(userService.save(userDTO)>0) {
return ResponseResult.success("新增成功");
}
return ResponseResult.fail(ErrorCodeEnum.INSERT_FIILURE);
}
@PutMapping("/{id}")
public ResponseResult update(@NotNull(message = "用户id不能为空") @PathVariable("id")Long id, @RequestBody UserDTO userDTO) {
if(userService.update(id, userDTO)>0) {
return ResponseResult.success("更新成功");
}
return ResponseResult.fail(ErrorCodeEnum.UPDATE_FIILURE);
}
如果是对象类型的参数用@Validated,如果是简单类型的参数直接在参数前面写校验注解,但是需要在controller上加@Validated注解。

使用postman测试,发现已经被拦截下来了,返回400。这是controller的校验。接下来写service层的校验
@Data
public class PageQuery<T> implements Serializable{
private static final long serialVersionUID = 2220927012679224629L;
@NotNull(message = "页码不能为空")
@Min(message = "不能小于1", value = 1)
private Integer pageNo=1;
@NotNull(message = "每页条数不能为空")
@Max(message = "不能大于100", value = 100)
private Integer pageSize=20;
private T query;
}
public class VaildatorUtils {
private static Validator validator=Validation.buildDefaultValidatorFactory().getValidator();
public static<T> void validate(T object,Class... groups) {
Set<ConstraintViolation<T>> validate=validator.validate(object, groups);
if(!CollectionUtils.isEmpty(validate)) {
StringBuilder exceptionMessage=new StringBuilder();
validate.forEach(constraintViolation -> {
exceptionMessage.append(constraintViolation.getMessage());
});
throw new RuntimeException(exceptionMessage.toString());
}
}
}
@Override
public PageResult<List<UserDTO>> query(PageQuery<UserQueryDTO> pageQuery) {
// 手动校验功能
VaildatorUtils.validate(pageQuery);
Page page=new Page(pageQuery.getPageNo(),pageQuery.getPageSize());
UserDO query=new UserDO();
BeanUtils.copyProperties(pageQuery.getQuery(), query);
QueryWrapper queryWrapper=new QueryWrapper(query);
IPage<UserDO> userDOIPage =userMapper.selectPage(page,queryWrapper);
// 结果解析
PageResult pageResult=new PageResult();
pageResult.setPageNo((int) userDOIPage.getCurrent());
pageResult.setPageSize((int) userDOIPage.getSize());
pageResult.setTotal(userDOIPage.getTotal());
pageResult.setPageNum(userDOIPage.getPages());
List<UserDTO> useDTOList=Optional.ofNullable(userDOIPage.getRecords())
.map(List::stream)
.orElseGet(Stream::empty)
.map(userDO -> {
UserDTO userDTO = new UserDTO();
BeanUtils.copyProperties(userDO, userDTO);
return userDTO;
}).collect(Collectors.toList());
pageResult.setData(useDTOList);
return pageResult;
}
统一异常处理
之前可以看到,参数校验是直接抛出异常到前端了。非常不友好,可以使用异常处理来改造。
/**
* 全局异常处理
* @author user
*
*/
@ResponseBody
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
/**
* 捕获参数校验错误
* @param ex
* @param request
* @return
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public final ResponseResult methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException ex, WebRequest request) {
List<ObjectError> allErrors = ex.getBindingResult().getAllErrors();
Object[] messages = allErrors.stream().map(ObjectError::getDefaultMessage).toArray();
return ResponseResult.fail(ErrorCodeEnum.PARAM_ERROR.getCode(), Arrays.toString(messages));
}
@ExceptionHandler(BindException.class)
public final ResponseResult bindExceptionHandler(BindException ex, WebRequest request) {
List<ObjectError> allErrors = ex.getAllErrors();
Object[] messages = allErrors.stream().map(ObjectError::getDefaultMessage).toArray();
return ResponseResult.fail(ErrorCodeEnum.PARAM_ERROR.getCode(), Arrays.toString(messages));
}
@ExceptionHandler(value = RuntimeException.class)
public ResponseResult runtimeExceptionHandler(RuntimeException e) {
log.error("运行时异常",e);
return ResponseResult.fail(ErrorCodeEnum.UNKNOWN_ERROR.getCode(), e.getMessage());
}
@ResponseBody
@ExceptionHandler(value = Throwable.class)
public ResponseResult throwableHandler(Throwable e) {
log.error("系统级异常",e);
return ResponseResult.fail(ErrorCodeEnum.SYSTEM_ERROR.getCode(), e.getMessage());
}
}



级联验证

@Size :验证list是否符合要求
@Valid:验证list中的userinfo是否符合要求
自定义校验注解
/**
* 自定义手机号约束注解
* @author user
*
*/
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
// 约束注解关联的验证器
@Constraint(validatedBy = PhoneValidator.class)
public @interface Phone {
String message() default "手机号验证错误";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class PhoneValidator implements ConstraintValidator<Phone, String>{
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
// 实现自定义逻辑
if(value!=null && value.trim().length()==11) {
return true;
}
return false;
}
}
网友评论