Bean Validation是Java定义的一套基于注解的数据校验规范,比如@Null、@NotNull、@Pattern等,它们位于 javax.validation.constraints这个包下。
hibernate validator是对这个规范的实现,并增加了一些其他校验注解,如 @NotBlank、@NotEmpty、@Length等,它们位于org.hibernate.validator.constraints这个包下。
依赖
hibernate validator框架已经集成在 spring-boot-starter-web中,所以无需再添加其他依赖。如果不是Spring Boot项目,需要添加如下依赖。
@Valid和@Validated 区别
Spring Validation验证框架对参数的验证机制提供了@Validated(Spring's JSR-303规范,是标准JSR-303的一个变种)。
javax提供了@Valid,配合BindingResult可以直接提供参数验证结果(标准JSR-303规范)。
@Validation对@Valid进行了二次封装,在使用上并没有区别,但在分组、注解位置、嵌套验证等功能上有所不同
分组
@Validated:提供了一个分组功能,可以在入参验证时,根据不同的分组采用不同的验证机制。
@Valid:没有分组校验的功能。
注解地方
@Validated:用在类型、方法和方法参数上(类, 方法, 参数)。但不能用于成员属性。
@Valid:可以用在方法、构造函数、方法参数和成员属性上(方法, 构造器, 参数,字段, 泛型),可以用@Valid实现嵌套验证
两者是否能用于成员属性(字段)上直接影响能否提供嵌套验证的功能
如A类中引用B类,且A、B二类都有内部校验,为了使B类也生效,在A类中引用B类时,在B类变量上加@Valid注解,如果B类为集合等类型且不能为空还需要再加@NotEmpty。
BindingResult
BindingResult用在实体类校验信息返回结果绑定。
该类作为方法入参,要写在实体对象后面。
-
@PostMapping("/menus")
-
public Result
addMenu
(@RequestBody @Valid Menu menu, BindingResult result) {}
validator内置注解
hibernate validator扩展注解
分类
空与非空
注解 |
支持Java类型 |
说明 |
@Null |
Object |
为null |
@NotNull |
Object |
不为null |
CharSequence |
不为null,且必须有一个非空格字符 |
|
@NotEmpty |
CharSequence、Collection、Map、Array |
不为null,且不为空(length/size>0) |
Boolean
注解 |
支持Java类型 |
说明 |
备注 |
@AssertTrue |
boolean、Boolean |
为true |
为null有效 |
@AssertFalse |
boolean、Boolean |
为false |
为null有效 |
日期
注解 |
支持Java类型 |
说明 |
备注 |
@Future |
Date、 Calendar、 Instant、 LocalDate、 LocalDateTime、 LocalTime、 MonthDay、 OffsetDateTime、 OffsetTime、 Year、 YearMonth、 ZonedDateTime、 HijrahDate、 JapaneseDate、 MinguoDate、 ThaiBuddhistDate |
验证日期为当前时间之后 |
为null有效 |
@FutureOrPresent |
Date、 Calendar、 Instant、 LocalDate、 LocalDateTime、 LocalTime、 MonthDay、 OffsetDateTime、 OffsetTime、 Year、 YearMonth、 ZonedDateTime、 HijrahDate、 JapaneseDate、 MinguoDate、 ThaiBuddhistDate |
验证日期为当前时间或之后 |
为null有效 |
@Past |
Date、 Calendar、 Instant、 LocalDate、 LocalDateTime、 LocalTime、 MonthDay、 OffsetDateTime、 OffsetTime、 Year、 YearMonth、 ZonedDateTime、 HijrahDate、 JapaneseDate、 MinguoDate、 ThaiBuddhistDate |
验证日期为当前时间之前 |
为null有效 |
@PastOrPresent |
Date、 Calendar、 Instant、 LocalDate、 LocalDateTime、 LocalTime、 MonthDay、 OffsetDateTime、 OffsetTime、 Year、 YearMonth、 ZonedDateTime、 HijrahDate、 JapaneseDate、 MinguoDate、 ThaiBuddhistDate |
验证日期为当前时间或之前 |
为null有效 |
数值
注解 |
支持Java类型 |
说明 |
备注 |
@Max |
BigDecimal、BigInteger, byte、short、int、long以及包装类 |
小于或等于 |
为null有效 |
@Min |
BigDecimal、BigInteger, byte、short、int、long以及包装类 |
大于或等于 |
为null有效 |
@DecimalMax |
BigDecimalBigInteger、CharSequence, byte、short、int、long以及包装类 |
小于或等于 |
为null有效 |
@DecimalMin |
BigDecimal、BigIntegerCharSequence, byte、short、int、long以及包装类 |
大于或等于 |
为null有效 |
@Negative |
BigDecimal、BigInteger, byte、short、int、long、float、double以及包装类 |
负数 |
为null有效,0无效 |
@NegativeOrZero |
BigDecimal、BigInteger, byte、short、int、long、float、double以及包装类 |
负数或零 |
为null有效 |
@Positive |
BigDecimal、BigInteger, byte、short、int、long、float、double以及包装类 |
正数 |
为null有效,0无效 |
@PositiveOrZero |
BigDecimal、BigInteger, byte、short、int、long、float、double以及包装类 |
正数或零 |
为null有效 |
@Digits(integer = 3, fraction = 2) |
BigDecimal、BigInteger、CharSequence, byte、short、int、long以及包装类 |
整数位数和小数位数上限 |
为null有效 |
@Length |
String |
字符串长度范围 |
@Length |
@Range |
数值类型和String |
指定范围 |
@Range |
其他
注解 |
支持Java类型 |
说明 |
备注 |
@Pattern |
CharSequence |
匹配指定的正则表达式 |
为null有效 |
CharSequence |
邮箱地址 |
为null有效,默认正则 '.*' |
|
@Size |
CharSequence、Collection、Map、Array |
大小范围(length/size>0) |
为null有效 |
@URL |
URL地址验证 |
@URL |
单参数校验
需要在参数前添加注解,而且controller类上必须添加@Validated注解。
-
@RestController
-
@RequestMapping("/menu")
-
@Validated
// 单参数校验需要加的注解
-
public
class
SysMenuController {
-
@DeleteMapping("/menus")
-
public Result
deleteMenu
(@NotNull(message = "id不能为空") Long id) {
-
}
-
}
对象参数校验
先在对象的校验属性上添加注解,然后在Controller方法的对象参数前添加@Valid、@Validated
-
// 对象
-
public
class
Menu {
-
private Long menuId;
-
@NotNull(message =parentId不能为空")
-
private Long parentId;
-
}
-
@PostMapping("/menus")
-
public Result
addMenu
(@RequestBody @Valid Menu menu, BindingResult result) {
-
}
对象嵌套
-
// 对象
-
public
class
PagedQueryReqBody<T> {
-
private Integer page_no;
-
private Integer page_row_no;
-
@NotNull
-
private String page_flg;
-
@Valid
-
private T data_request;
-
}
-
-
public
class
DataReqPQ {
-
@NotNull
-
private String car_no;
-
}
-
// 接口
-
@PostMapping(value = "/queryParameter")
-
public Result
queryParameter
(@RequestBody @Validated PagedQueryReqBody<DataReqPQ> requestMsg, BindingResult result){
-
}
分组校验
新建组
Validated有自己默认的组 Default.class
-
public
interface
Update {
-
}
-
public
interface
Add
extends
Default {
-
}
-
// 对象
-
public
class
User {
-
@NotBlank(message = "id不能为空",groups = {Update.class})
-
private String id;
-
private String name;
-
@NotBlank(message = "密码不能为空",groups = {Add.class})
-
private String password;
-
}
id属性的校验属于Update分组的校验
password属性的校验属于Add、Default分组的校验
使用分组
使用默认分组:Add分组继承Default,所以校验password,不校验id
-
@PostMapping("/addUser")
-
public Resp
addUser
(@Validated @RequestBody User uer) {
-
}
使用Update分组:只校验id,不校验password
-
@PostMapping("/updateUser")
-
public Resp
updateUser
(@Validated(Update.class) @RequestBody User user) {
-
}
全局异常处理类
缺少参数抛出的异常是MissingServletRequestParameterException
单参数校验失败后抛出的异常是ConstraintViolationException
get请求的对象参数校验失败后抛出的异常是BindException
post请求的对象参数校验失败后抛出的异常是MethodArgumentNotValidException
不同异常对象的结构不同,对异常消息的提取方式也就不同。
-
@Slf4j
-
@RestControllerAdvice
-
public
class
ExceptionAdvice {
-
@ResponseBody
-
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
// 设置状态码为500
-
@ExceptionHandler(MethodArgumentNotValidException.class)
-
public String
postExceptionHandler
(MethodArgumentNotValidException e){
-
log.error(
"执行异常",e);
-
BindingResult
exceptions
= e.getBindingResult();
-
if (exceptions.hasErrors()) {}
-
}
-
-
@ResponseBody
-
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
// 设置状态码为500
-
@ExceptionHandler(ConstraintViolationException.class)
-
public String
paramExceptionHandler
(ConstraintViolationException e){
-
log.error(
"执行异常",e);
-
-
}
-
}
BindingResult异常
Controller方法的中处理
-
@PostMapping("addUser")
-
public Result
addUser
(@RequestBody @Valid User user,BindingResult result){
-
//校验到错误
-
if (result.hasErrors()) {
-
//获得错误信息列表
-
List<String> errMsgs = result.getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(toList());
-
String
lists
= StringUtils.join(lists,
";");
-
return
new
Result(“”
"", lists);
-
}
-
return
new
Result(“”,
"",
null);
-
}
AOP校验
-
/**
-
*将此注解加在需要进行参数校验的方法上,
-
*/
-
@Target(ElementType.METHOD)
-
@Retention(RetentionPolicy.RUNTIME)
-
public
@interface ParamValid {
-
}
-
@Aspect
-
@Component
-
public
class
ParamValidAspect {
-
-
private
static
final
Logger
log
= LoggerFactory.getLogger(ParamValidAspect.class);
-
-
@Before("@annotation(paramValid)")
-
public
void
paramValid
(JoinPoint point, ParamValid paramValid){
-
Object[] paramObj = point.getArgs();
-
if (paramObj.length >
0){
-
Arrays.stream(paramObj).forEach(
-
e ->{
-
if (e
instanceof BindingResult) {
-
BindingResult
result
= (BindingResult) e;
-
Result
errorMap
=
this.validRequestParams(result);
-
if (errorMap !=
null){
-
ServletRequestAttributes
res
= (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
-
HttpServletResponse
response
= res.getResponse();
-
response.setCharacterEncoding(
"UTF-8");
-
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
-
response.setStatus(HttpStatus.BAD_REQUEST.value());
-
-
OutputStream
output
=
null;
-
try {
-
output = response.getOutputStream();
-
String
error
= objectMapper.writeValueAsString(errorMap);
-
//响应错误信息
-
output.write(error.getBytes(
"UTF-8"));
-
}
catch (IOException e){
-
log.error(e.getMessage());
-
}
finally{
-
try{
-
if (output !=
null){
-
output.close();
-
}
-
}
catch (IOException e) {
-
log.error(e.getMessage());
-
}
-
}
-
}
-
}
-
});
-
}
-
}
-
-
/**
-
* 校验
-
*/
-
private Result
validRequestParams
(BindingResult result) {
-
if (result.hasErrors()) {
-
List<String> errMsgs = result.getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(toList());
-
String
lists
= StringUtils.join(lists,
";");
-
return
new
Result(
"",
"", lists);
-
}
-
return
null;
-
}
-
}
转载:https://blog.csdn.net/csdnzhang365/article/details/129141189