使用@validated注解校验参数

准备工作

maven依赖

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-validation</artifactId>
		</dependency>

新建参数对象:TestValidateDTO

@Data
@ToString
public class TestValidateDTO {

    @NotBlank
    private String name;

    @Email
    private String email;

    @Pattern(regexp = "0?(13|14|15|17|18|19)[0-9]{9}")
    private String phone;

    @NotBlank
    private String word;
}

调用关系

@RestController
@RequestMapping("/validate")
public class TestValidateWeb {

    @Autowired
    private TestValidateService tvs;

    @PostMapping("/test1")
    public String test1(@Validated @RequestBody TestValidateDTO dto){
        return tvs.test1(dto);
    }
}
@Service
public class TestValidateService {

    public String test1(TestValidateDTO dto){
        return dto.toString();
    }
}

测试效果

正常数据验证:

在这里插入图片描述

修改name,email,phone,word的内容,故意不符合规则:

在这里插入图片描述

在这里插入图片描述

虽然有报错但是报错提示400,很不人性化,继续修改报错提示。

修改抛出异常提示

@RestControllerAdvice
public class ResponseException {

    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public String  validateException(MethodArgumentNotValidException exc){
        return "出现问题:"+exc.getBindingResult().getFieldError().getDefaultMessage();
    }

    @ExceptionHandler(value = Exception.class)
    public String  exception(Exception exc){
        return "出现问题:"+exc.getMessage();
    }
}
@Data
@ToString
public class TestValidateDTO {

    @NotBlank(message = "姓名不能为空")
    private String name;

    @Email(message = "不是符合格式的邮箱")
    private String email;

    @Pattern(regexp = "0?(13|14|15|17|18|19)[0-9]{9}", message = "手机号码不符合格式")
    private String phone;

    @NotBlank(message = "说句话")
    private String word;
}

在参数对象的注解上加上message。

在这里插入图片描述

符合预期效果。

分组校验

同样的一个参数对象,可能两个接口对参数对象的校验规则是不一样的,可以使用groups实现在同一个参数对象上不同接口有不同的校验规则。

新建一个类,里面有两个内部接口,新建内部接口是因为**@Validated**(**value **= {})中value只接受接口类型

public class TestValidateGroups {

    public interface  test1{};

    public interface test2 {};


}

接口参数中加上**@Validated**(**value **= {TestValidateGroups.test2.class}),不同接口加上不同内部接口。

@RestController
@RequestMapping("/validate")
@Validated
public class TestValidateWeb {

    @Autowired
    private TestValidateService tvs;

    @PostMapping("/test1")
    public String test1(@Validated(value = {TestValidateGroups.test1.class}) @RequestBody TestValidateDTO dto) {
        return tvs.test1(dto);
    }

    @PostMapping("/test2")
    public String test2(@Validated(value = {TestValidateGroups.test2.class}) @RequestBody TestValidateDTO dto) {
        return tvs.test1(dto);
    }
}

参数对象中name和word指定内部接口,和接口层的关系对应上。

@Data
@ToString
public class TestValidateDTO {

    @NotBlank(message = "姓名不能为空", groups = {TestValidateGroups.test1.class})
    private String name;

    @Email(message = "不是符合格式的邮箱")
    private String email;

    @Pattern(regexp = "0?(13|14|15|17|18|19)[0-9]{9}", message = "手机号码不符合格式")
    private String phone;

    @NotBlank(message = "说句话", groups = {TestValidateGroups.test2.class})
    private String word;
}

调用这两个接口,参数对象都一样:

在这里插入图片描述

在这里插入图片描述

可以看到参数对象都是一样的,但是提示是不一样的,说明校验规则是做了区分的。

❗注意,如果这个时候我在email属性上加了如下的规则

@NotBlank(message = “邮箱不能为空”, groups = {TestValidateGroups.test1.class, TestValidateGroups.test2.class})
@Email(message = “不是符合格式的邮箱”)
private String email;

因为**@Email没有指定分组,所以不管如何调用都不会校验是否是邮箱,@NotBlank因为加了指定分组所以才会校验是否为空。**

出现这个问题,是因为在test1接口指定了TestValidateGroups.test1.class分组,如果在test1接口中将指定分组删掉:

@RestController
@RequestMapping("/validate")
@Validated
public class TestValidateWeb {

    @Autowired
    private TestValidateService tvs;

    @PostMapping("/test1")
    public String test1(@Validated @RequestBody TestValidateDTO dto) {
        return tvs.test1(dto);
    }

    @PostMapping("/test2")
    public String test2(@Validated(value = {TestValidateGroups.test2.class}) @RequestBody TestValidateDTO dto) {
        return tvs.test1(dto);
    }
}

在这里插入图片描述

这个时候也只会校验在参数对象中没有指定分组的属性。改变phone也是会校验的。

自定义注解校验规则

@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER,})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = TestAnnotationValidateImpl.class)
@Repeatable(TestAnnotation.List.class)
public @interface TestAnnotation {

    String message() default "字段不符合规范";

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

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

    @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.PARAMETER,})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface List {
        TestAnnotation[] value();
    }
}

这里出现的@Target注解指定的用途需要一致,个数一样,值一样,不然会提示错误。

@Component
public class TestAnnotationValidateImpl implements ConstraintValidator<TestAnnotation, TestValidateDTO> {
    @Override
    public void initialize(TestAnnotation constraintAnnotation) {
        ConstraintValidator.super.initialize(constraintAnnotation);
    }

    @Override
    public boolean isValid(TestValidateDTO testValidateDTO, ConstraintValidatorContext constraintValidatorContext) {
        if (testValidateDTO == null) {
            return true;
        }
        if (StringUtils.isBlank(testValidateDTO.getWord())) {
            return false;
        }
//      返回false就会抛出message
        return !testValidateDTO.getWord().contains("c") && !testValidateDTO.getWord().contains("c") &&
                !testValidateDTO.getWord().contains("cnm");
    }
}
@RestController
@RequestMapping("/validate")
@Validated
public class TestValidateWeb {

    @Autowired
    private TestValidateService tvs;

    @PostMapping("/test1")
    public String test1(@Validated @RequestBody TestValidateDTO dto) {
        return tvs.test1(dto);
    }

    @PostMapping("/test2")
    public String test2(@Validated(value = {TestValidateGroups.test2.class}) @TestAnnotation(message = "cnm,别说脏话啊") @RequestBody TestValidateDTO dto) {
        return tvs.test1(dto);
    }
}

在test2接口上加上TestAnnotation注释,因为我将TestAnnotation注解设置为可以用在参数上。

在这里插入图片描述

效果符合预期。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值