对于一些业务场景,接口字段很多,且对字段格式有特殊要求、或者各个字段之间有级联关系,为了写出优雅的代码,避免接口字段校验逻辑对业务主体逻辑的侵入,考虑使用注解方式去校验字段,当然也支持自定义注解。
使用Spring Validation注解实现对接口入参的校验。
接下来展示,使用自定义注解校验入参某几个字段之间的校验,例如入参的“省、市、区”的级联校验。
3.1 项目结构
3.2 自定义注解
- package com.saferycom.validate;
-
- import javax.validation.Constraint;
- import javax.validation.Payload;
- import java.lang.annotation.*;
-
- /**
- * @author LH
- */
- @Documented
- @Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
- @Retention(RetentionPolicy.RUNTIME)
- @Constraint(validatedBy = PccMatchValidator.class)
- public @interface PccMatch {
-
- // 省
- String prov();
- // 市
- String city();
- // 区
- String country();
-
- // 默认错误消息
- String message() default "Invalid value for MyConstraint";
-
- // 分组
- Class>[] groups() default {};
-
- // 负载
- Class extends Payload>[] payload() default {};
-
- }
3.3 自定义校验器
- package com.saferycom.validate;
-
- import org.springframework.beans.BeanWrapperImpl;
-
- import javax.validation.ConstraintValidator;
- import javax.validation.ConstraintValidatorContext;
-
- /**
- * @author LH
- */
- public class PccMatchValidator implements ConstraintValidator
{ - private String prov;
- private String city;
- private String country;
-
- @Override
- public void initialize(PccMatch args) {
- // 初始化逻辑
- this.prov = args.prov();
- this.city = args.city();
- this.country = args.country();
- }
-
-
- @Override
- public boolean isValid(Object obj, ConstraintValidatorContext context) {
- // 结构体为空的默认返回true
- if (obj == null) {
- return true;
- }
-
- BeanWrapperImpl beanWrapper = new BeanWrapperImpl(obj);
- String province = (String) beanWrapper.getPropertyValue(this.prov);
- String city1 = (String) beanWrapper.getPropertyValue(this.city);
- String country1 = (String) beanWrapper.getPropertyValue(this.country);
-
- // todo 这里可以添加具体的验证逻辑,这里简单示例
- if ("31000".equals(province) && "31100".equals(city1) && "31101".equals(country1)) {
- return true;
- }
-
- return false;
- }
- }
3.4 定义一个接口的入参,对需要校验的字段打上自定义注解
- package com.saferycom.validate;
-
- import lombok.AllArgsConstructor;
- import lombok.Builder;
- import lombok.Data;
- import lombok.NoArgsConstructor;
-
- import javax.validation.Valid;
-
- /**
- * @author LH
- */
- @Data
- @Builder
- @AllArgsConstructor
- @NoArgsConstructor
- @Valid
- @PccMatch(prov = "prov", city = "city", country = "country", message = "省市区级联校验失败")
- public class RegisterAddress {
- private String id;
- private String prov;
- private String city;
- private String country;
- }
3.5 定义一个接口,进行测试
- package com.saferycom.validate;
-
- import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
- import org.springframework.web.bind.annotation.PostMapping;
- import org.springframework.web.bind.annotation.RequestBody;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RestController;
-
- import javax.annotation.Resource;
- import javax.validation.ConstraintViolation;
- import java.util.Set;
-
- /**
- * @author LH
- */
- @RestController
- @RequestMapping("/test")
- public class ValidateTestController {
-
- @Resource
- private LocalValidatorFactoryBean localValidatorFactoryBean;
-
- @PostMapping("/validate")
- public String validateTest(@RequestBody RegisterAddress address) {
-
- Set
> violations = localValidatorFactoryBean.getValidator().validate(address); -
- return "ok";
- }
- }
3.5 启动服务,使用ApiPost调用接口查看校验情况
断点测试,查看校验结果
如上图,我们拿到的校验结果。
以上,就是使用自定义注解实现接口入参校验,后续,我们校验完成之后,可以考虑抛出异常,然后对抛出的异常进行统一收集处理,然后统一返回给接口调用者,这里的操作我们放到下期进行展示。
期待一下吧,一起学习,一起进步。