SpringBoot参数校验hibernate-validator使用
一、介绍
如果我们的项目使用了Spring Boot,hibernate validator框架已经集成在 spring-boot-starter-web中,所以无需再添加其他依赖。如果不是Spring Boot项目,则需要添加如下依赖:
hibernate-validator参数检验不生效问题解决:主要是依赖的版本号大版本为6即可生效
https://blog.csdn.net/u012978272/article/details/120009157
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.2.5.Final</version>
</dependency>
常用注解介绍
注解 | 作用类型 | 来源 | 说明 |
---|---|---|---|
@Null | 任何类型 | 属性必须为null | |
@NotNull | 任何类型 | 属性不能为null | |
@NotEmpty | 集合 | hibernate validator扩展注解 | 集合不能为null,且size大于0 |
@NotBlank | 字符串、字符 | hibernate validator扩展注解 | 字符类不能为null,且去掉空格之后长度大于0 |
@AssertTrue | Boolean、boolean | 布尔属性必须是true | |
@Min | 数字类型(原子和包装) | 限定数字的最小值(整型) | |
@Max | 同@Min | 限定数字的最大值(整型) | |
@DecimalMin | 同@Min | 限定数字的最小值(字符串,可以是小数) | |
@DecimalMax | 同@Min | 限定数字的最大值(字符串,可以是小数) | |
@Range | 数字类型(原子和包装) | hibernate validator扩展注解 | 限定数字范围(长整型) |
@Length(min=,max=) | 字符串 | hibernate validator扩展注解 | 限定字符串长度 |
@Size | 集合 | 限定集合大小 | |
@Past | 时间、日期 | 必须是一个过去的时间或日期 | |
@Future | 时期、时间 | 必须是一个未来的时间或日期 | |
字符串 | hibernate validator扩展注解 | 必须是一个邮箱格式 | |
@Pattern | 字符串、字符 | 正则匹配字符串 |
二、校验方式
1、单个参数校验
Controller类上必须添加@Validated注解
(不加验证就不会生效),如下所示:
@Validated
@RestController
@RequestMapping("/user")
public class UserController {
// do something...
}
方法参数前面加上注解即可, 如下所示:
import javax.validation.constraints.NotNull;
/**
* 推荐这种方式
* 缺少参数时会抛出异常:javax.validation.ConstraintViolationException
*/
public Result test1(@NotNull(message = "id不能为空") Long id) {
//
}
/**
* 不推荐使用
* @RequestParam 注解也会对参数进行校验
* 如果参数id没传,则会抛出:javax.servlet.ServletException 异常
* 这种情况想要跳过校验可添加 javax.annotation.Nullable 注解到参数上
*/
public Result test2(@RequestParam Long id) {
// do something
}
/**
* 不推荐使用
* 此种情况不对 id 参数进行检查,传值就有,没传就是null
*/
public Result test3(Long id) {
// do something
}
2、 对象参数校验
对象参数校验使用时,需要先在对象的校验属性上添加注解,然后在Controller方法的对象参数前添加@Validated
注解,如下所示:
public Result addUser(@RequestBody @Validated UserDto userDto) {
// do something
}
public class UserDto {
@NotBlank(message="名称不能为空")
private String name;
@NotNull(message="年龄不能为空")
private Integer age;
}
3、 嵌套对象校验
如果需要校验的参数对象中还嵌套有一个对象属性,而该嵌套的对象属性也需要校验,那么就需要在该对象属性上增加@Valid
注解。
public class UserDto {
@NotNull
private Long id;
@NotBlank
private String name;
@NotNull
private Integer age;
@Valid
private Phone phone;
}
public class Phone {
@NotBlank
private String type;
@NotBlank
private String phoneNum;
}
4、分组校验
在对象参数校验场景下,有一种特殊场景,同一个参数对象在不同的场景下有不同的校验规则。比如,在创建对象时不需要传入id字段(id字段是主键,由系统生成,不由用户指定),但是在修改对象时就必须要传入id字段。在这样的场景下就需要对注解进行分组。
1)组件默认有个分组Default.class
, 所以我们可以再创建一个分组例如UpdateAction.class
,如下所示:
public interface UpdateAction {
}
2)在参数类中需要校验的属性上,在注解中添加groups
属性(表示Default.class默认情况下会校验name,age参数,而在UpdateAction.class情况下会校验id参数),如下所示:
public class UserDto {
@NotNull(groups = {UpdateAction.class}, message = "id不能为空")
private Long id;
@NotBlank
private String name;
@NotNull
private Integer age;
}
3)在Controller的方法中,在@Validated
注解里指定哪种场景即可(没有指定就代表采用Default.class
,采用其他分组则需要显示指定)。如下代码便表示在addUser()接口中按照默认情况进行参数校验,在updateUser()接口中按照默认情况和UpdateAction分组对参数进行共同校验。
public Result addUser(@Validated UserDto userDto) {
// do something
}
public Result updateUser(@Validated({Default.class, UpdateAction.class}) UserDto userDto) {
// do something
}
5、枚举校验
枚举校验hibernate validator并不支持。需要自己扩展实现
1) 定义EnumChck注解,如下所示:
@Documented
@Constraint(validatedBy = EnumConstraintValidator.class)
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Repeatable(EnumCheck.List.class)
public @interface EnumCheck {
String message() default "{javax.validation.constraints.EnumCheck.message}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
/**
* 枚举类
*
*/
Class<? extends EnumValidator> clazz();
/**
* 调用的方法名称
*/
String method() default "getValue";
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Documented
@interface List {
EnumCheck[] value();
}
}
2) 定义EnumValidator接口,让需要校验的枚举类实现其接口的getValue()方法,如下所示:
public interface EnumValidator {
Object getValue();
}
3)自定义实现EnumConstraintValidator,需要实现ConstraintValidator接口,如下所示:
public class EnumConstraintValidator implements ConstraintValidator<EnumCheck, Object> {
/**
* 注解对象
*/
private EnumCheck annotation;
/**
* 初始化方法
*
* @param constraintAnnotation 注解对象
*/
@Override
public void initialize(EnumCheck constraintAnnotation) {
this.annotation = constraintAnnotation;
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
if (Objects.isNull(value)) {
return false;
}
Object[] enumConstants = annotation.clazz().getEnumConstants();
try {
Method method = annotation.clazz().getMethod(annotation.method());
for (Object enumConstant : enumConstants) {
if (value.equals(method.invoke(enumConstant))) {
return true;
}
}
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
return false;
}
}
4) 具体使用步骤如下:
具体枚举类实现上面定义的EnumValidator接口:
public enum RefNodeType implements EnumValidator {
PROJECT("project", "项目"),
FIELD("field", "变量"),
FUNC("func", "函数");
private final String code;
private final String label;
RefNodeType(String code, String label) {
this.code = code;
this.label = label;
}
public String getCode() {
return code;
}
public String getLabel() {
return label;
}
@Override // 需要实现getValue方法
public Object getValue() {
return code;
}
}
参数校验加上@EnumCheck枚举校验注解,如下所示:
public class TestDto {
@NotBlank(message = "变量类型不能为空")
// 加上枚举校验注解
@EnumCheck(clazz = RefNodeType.class, message = "变量类型不合法")
private String sourceType;
}
6、正则通用校验
1)定义RegRule接口,将需要用到的正则表达式定义为接口属性,如下所示:
public interface RegRule {
String MOBILE = "^(((13[0-9])|(14[579])|(15([0-3]|[5-9]))|(16[6])|(17[0135678])|(18[0-9])|(19[89]))\\d{8})$";
}
2)对象属性加上@Pattern注解,如下所示:
public class UserDto {
@Pattern(regexp = RegRule.MOBILE, message = "手机号格式不正确")
private String mobile;
}
三、各类异常捕获处理
参数校验失败后会抛出异常,只需要在全局异常处理类中捕获参数校验的失败异常,然后将错误消息添加到返回值中即可。捕获异常的方法如下所示,返回值AjaxResult
是我们系统自定义的返回值类。
注意:如果存在实现了WebMvcConfigurer的配置类,并且重写了configureHandlerExceptionResolvers方法,那么异常信息将会在configureHandlerExceptionResolvers方法中被捕获处理,就不会走此处的逻辑
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author JHL
* @version 1.0
* @since : JDK 11
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 全局异常处理
*/
@ExceptionHandler(Exception.class)
public AjaxResult handleException(Exception e, HttpServletRequest request, HttpServletResponse response) {
// do something
AjaxResult result = null;
}
}
1)缺少参数抛出的异常是MissingServletRequestParameterException
,如下所示:
if (e instanceof MissingServletRequestParameterException) {
String msg = MessageFormat.format("缺少参数{0}", ((MissingServletRequestParameterException) e).getParameterName());
result = AjaxResult.error(HttpStatus.HTTP_BAD_REQUEST, msg);
}
2)单参数校验失败后抛出的异常是ConstraintViolationException
,如下所示:
if (e instanceof ConstraintViolationException){
String msg = e.getMessage().split(":")[1].trim();
result = AjaxResult.error(HttpStatus.HTTP_BAD_REQUEST, msg);
}
3)GET
请求的对象参数校验失败后抛出的异常是 BindException
,如下所示:
if (e instanceof BindException){
result = AjaxResult.error(HttpStatus.HTTP_BAD_REQUEST, e.getMessage());
}
4)POST
请求的对象参数校验失败后抛出的异常是MethodArgumentNotValidException
,如下所示:
if (e instanceof MethodArgumentNotValidException){
// 四个中文以上的异常信息才会被匹配
String msg = ReUtil.findAll("[\u4e00-\u9fa5]{4,}", e.getMessage(), 0).get(0);
result = new AjaxResult(HttpStatus.HTTP_BAD_REQUEST, msg);
}
原文地址:
https://www.cnblogs.com/itsharehome/p/15425885.html
@Validated注解不生效问题:https://blog.csdn.net/qiuxuezhe_fei/article/details/128197714
· 分享4款.NET开源、免费、实用的商城系统
· Obsidian + DeepSeek:免费 AI 助力你的知识管理,让你的笔记飞起来!
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
2021-06-28 postman测试上传文件接口