自定义注解实现入参校验

一、需求背景

对于一些业务场景,接口字段很多,且对字段格式有特殊要求、或者各个字段之间有级联关系,为了写出优雅的代码,避免接口字段校验逻辑对业务主体逻辑的侵入,考虑使用注解方式去校验字段,当然也支持自定义注解。

二、实现方案

使用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<PccMatch, Object> {
    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<ConstraintViolation<RegisterAddress>> violations = localValidatorFactoryBean.getValidator().validate(address);

        return "ok";
    }
}

3.5 启动服务,使用ApiPost调用接口查看校验情况

 断点测试,查看校验结果

如上图,我们拿到的校验结果。 

四、总结

以上,就是使用自定义注解实现接口入参校验,后续,我们校验完成之后,可以考虑抛出异常,然后对抛出的异常进行统一收集处理,然后统一返回给接口调用者,这里的操作我们放到下期进行展示。

期待一下吧,一起学习,一起进步。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值