springboot参数校验-使用特性(自定义手机号码注解)

简单介绍

JSR303规范定义了Bean校验的标准validation-api,但没有实现。
hibernate validation则是对这个规范的实现。
Spring validation则是对hibernate validation的封装,方便在spring项目中使用

引入依赖

如果spring-boot版本小于2.3.x,spring-boot-starter-web会自动传入hibernate-validator依赖。如果spring-boot版本大于2.3.x,则需要手动引入依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>2.3.7.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.20</version>
</dependency>
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.0.1.Final</version>
</dependency>

编写实体类

在实体类上加校验注解,phone字段目前只有@NotNull,后期自定义注解校验手机号码格式

@Data
public class User {

    @NotNull
    @Length(min = 2, max = 12)
    private String name;

    @NotNull
    private String phone;

    @Email
    @NotNull
    private String email;
}

编写controller

@RestController
@RequestMapping("user")
public class UserController {

    @RequestMapping("/saveUser")
    public User saveUser(@Validated @RequestBody User user) {
        User userInfo = new User();
        BeanUtils.copyProperties(user, userInfo);
        return userInfo;
    }
}

启动类和配置就不展示了,接着postman测试,来看结果
在这里插入图片描述
postman会返回400状态码和"Bad Request",是因为系统抛出了 MethodArgumentNotValidException 方法参数异常,查看控制台可以看到:
在这里插入图片描述
但我们通常需要返回友好的格式给前端。
所以我们编写一个

返回类 R

@Data
public class R {

    private Boolean success;

    private Integer code;

    private String message;

    private Map<String, Object> data = new HashMap<>();

    private R() {}

    private static R ok() {
        R r = new R();
        r.setCode(ResultCode.SUCCESS);
        r.setMessage("成功");
        r.setSuccess(true);
        return r;
    }

    private static R error() {
        R r = new R();
        r.setSuccess(false);
        r.setCode(ResultCode.ERROR);
        r.setMessage("失败");
        return r;
    }

    private R data(String key, Object value) {
        this.data.put(key,value);
        return this;
    }

    private R data(Map<String, Object> data) {
        this.setData(data);
        return this;
    }

    private R success(boolean success) {
        this.setSuccess(success);
        return this;
    }

    public R message(String message) {
        this.setMessage(message);
        return this;
    }

    public R code(Integer code) {
        this.setCode(code);
        return this;
    }
}

interface ResultCode {

    public static Integer SUCCESS = 20000;

    public static Integer ERROR = 20001;

}

这个时候我们修改controller,并把@validated注解去掉,看看效果

@RestController
@RequestMapping("user")
public class UserController {

    @RequestMapping("/saveUser")
    public R saveUser(@Validated @RequestBody User user) {
        User userInfo = new User();
        BeanUtils.copyProperties(user, userInfo);
        return R.ok().data("data", userInfo);
    }
}

在这里插入图片描述
接着我们为了校验的时候也返回这个格式的数据,需要编写自定义异常,统一返回格式

@RestControllerAdvice
@Slf4j
public class ExceptionControllerAdvice {

    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public R handleValidException(MethodArgumentNotValidException e) {
        log.error("数据校验出现问题{},异常类型{}", e.getMessage(), e.getClass());
        BindingResult bindingResult = e.getBindingResult();
        HashMap<String, Object> errorMap = new HashMap<>();

        bindingResult.getFieldErrors().forEach(item->{
            System.out.println(item);
            errorMap.put(item.getField(), item.getDefaultMessage());
        });
        return R.error().data(errorMap);
    }
}

postman测试参数都不输入的情况下返回的数据:
在这里插入图片描述
再测试当 email 参数输入格式不对时返回的数据:
在这里插入图片描述
自此就完成了最基本的参数校验
接着来自定义校验注解来完成对手机号码的格式校验

自定义注解

编写一个注解

@Documented
@Constraint(
        validatedBy = {PhoneNumberConstraintValidator.class}
)
// 注解可以放在xx上
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PhoneNumber {

    // 注解的提示信息
    String message() default "电话号码格式不正确";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

编写一个类需要实现 ConstraintValidator 接口:

public class PhoneNumberConstraintValidator implements ConstraintValidator<PhoneNumber, String> {

    private static final Pattern PATTERN = Pattern.compile("^[1]\\d{10}$");

    @Override
    public void initialize(PhoneNumber constraintAnnotation) {

    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
        if (value != null) {
            Matcher matcher = PATTERN.matcher(value);
            return matcher.find();
        }
        return true;
    }
}

最后在实体类加上注解 @PhoneNumber

@NotNull
@PhoneNumber
private String phone;

最后postman测试,故意输错电话号码格式
在这里插入图片描述

分组校验

在实际应用中,多个方法使用的实体类可能是同一个,而不同方法的校验规则可能不一样,比如新增和修改,两个方法都使用 @RequestBody 接收实体参数,但是新增不需要传递id,而修改需要,这时就会产生冲突,可以设置分组校验来解决。

首先需要新增两个接口作为分组依据:saveUser updateUser

public interface SaveUser {
}
public interface UpdateUser {
}

然后修改实体类,加上分组

@Data
public class User {

    @NotNull(groups = UpdateUser.class)
    private String id;

    @NotNull(groups = {UpdateUser.class, SaveUser.class})
    @Length(min = 2, max = 12, groups = {UpdateUser.class, SaveUser.class})
    private String name;

    @NotNull(groups = {UpdateUser.class, SaveUser.class})
    @PhoneNumber(groups = {UpdateUser.class, SaveUser.class})
    private String phone;

    @Email(groups = {UpdateUser.class, SaveUser.class})
    @NotNull(groups = {UpdateUser.class, SaveUser.class})
    private String email;
}

修改controller

@RestController
@RequestMapping("user")
public class UserController {

    @RequestMapping("/saveUser")
    public R saveUser(@Validated(SaveUser.class) @RequestBody User user) {
        User userInfo = new User();
        BeanUtils.copyProperties(user, userInfo);
        return R.ok().data("data", userInfo).message("新增成功");
    }

    @RequestMapping("/updateUser")
    public R updateUser(@Validated(UpdateUser.class) @RequestBody User user) {
        User userInfo = new User();
        BeanUtils.copyProperties(user, userInfo);
        return R.ok().data("data", userInfo).message("修改成功");
    }
}

最后在postman中测试
当调用修改方法,参数中加上id值则显示成功,而不加上id则失败
在这里插入图片描述
在这里插入图片描述
当调用新增方法时即时没加上id也不会报错,因为设置了分组校验

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值