You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为不同接口的FilterDto指定对应数据库的校验规则?

动态选择数据库校验映射规则的实现方案

方案一:通过自定义校验注解传递映射规则

这种方案直接在控制器参数上指定对应接口的映射枚举,代码直观,耦合度低,适合接口数量较少的场景。

步骤1:改造自定义校验注解

修改@FilterValidatorBehaviour注解,添加mapping属性用于指定映射枚举:

@Target({ElementType.TYPE, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = FilterValidator.class)
public @interface FilterValidatorBehaviour {
    String message() default "无效的过滤条件";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
    
    // 新增:指定要使用的数据库映射枚举
    MappingEnum mapping() default MappingEnum.DB1_MAPPING;
}

步骤2:更新校验器实现类

在校验器的initialize方法中获取注解指定的映射枚举,用于后续校验逻辑:

public class FilterValidator implements ConstraintValidator<FilterValidatorBehaviour, FilterDto> {

    private MappingEnum targetMapping;

    @Override
    public void initialize(FilterValidatorBehaviour constraintAnnotation) {
        // 保存注解中指定的映射枚举
        this.targetMapping = constraintAnnotation.mapping();
    }

    @Override
    public boolean isValid(FilterDto filterDto, ConstraintValidatorContext ctx) {
        // 校验字段名是否为空
        if (filterDto.getName() == null) {
            ctx.disableDefaultConstraintViolation();
            ctx.buildConstraintViolationWithTemplate("过滤字段名不能为空")
               .addConstraintViolation();
            return false;
        }

        // 获取当前映射对应的字段类型配置
        DataBaseTypeObject typeObject = filterDto.getObjectWithFieldName(targetMapping);
        if (typeObject == null) {
            ctx.disableDefaultConstraintViolation();
            ctx.buildConstraintViolationWithTemplate("无效的过滤字段:" + filterDto.getName())
               .addConstraintViolation();
            return false;
        }

        // 校验值的类型是否匹配
        return typeObject.getType().equals(filterDto.getValue().getClass());
    }
}

步骤3:修改FilterDto类

移除类上的@FilterValidatorBehaviour注解,改为在控制器参数上动态指定:

@Getter
@Setter
public class FilterDto {
    String name;
    String operation;
    String value;

    @JsonIgnore
    public DataBaseTypeObject getObjectWithFieldName(MappingEnum mapping) {
        return mapping.getMappingMap().get(name);
    }
}

步骤4:更新控制器接口

在两个接口的List<FilterDto>参数上,添加带指定映射的注解:

// /v1/abc接口使用DB1映射规则
@PostMapping(path = "/v1/abc")
public PageableDto getAllSomething(
        @RequestAttribute UserClaim userClaim,
        @Valid SearchParametersDto searchParametersDto,
        @RequestBody @Valid @FilterValidatorBehaviour(mapping = MappingEnum.DB1_MAPPING) List<FilterDto> filters) {
    // 业务逻辑实现
}

// /v1/xyz接口使用DB2映射规则
@PostMapping(path = "/v1/xyz")
public PageableDto getAllAnotherThing(
        @RequestAttribute UserClaim userClaim,
        @Valid SearchParametersDto searchParametersDto,
        @RequestBody @Valid @FilterValidatorBehaviour(mapping = MappingEnum.DB2_MAPPING) List<FilterDto> filters) {
    // 业务逻辑实现
}

方案二:通过请求拦截器传递映射规则

这种方案通过拦截器根据请求路径自动设置映射规则,适合接口数量较多或路径有规律的场景。

步骤1:实现请求拦截器

根据请求URI设置对应的映射枚举到请求属性中:

public class DbMappingInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String uri = request.getRequestURI();
        if ("/v1/abc".equals(uri)) {
            request.setAttribute("DB_MAPPING", MappingEnum.DB1_MAPPING);
        } else if ("/v1/xyz".equals(uri)) {
            request.setAttribute("DB_MAPPING", MappingEnum.DB2_MAPPING);
        }
        return true;
    }
}

步骤2:注册拦截器

在Web配置类中注册拦截器,指定拦截的路径:

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new DbMappingInterceptor())
               .addPathPatterns("/v1/abc", "/v1/xyz");
    }
}

步骤3:更新校验器实现类

注入HttpServletRequest,从请求属性中获取映射枚举进行校验:

public class FilterValidator implements ConstraintValidator<FilterValidatorBehaviour, FilterDto> {

    @Autowired
    private HttpServletRequest request;

    @Override
    public boolean isValid(FilterDto filterDto, ConstraintValidatorContext ctx) {
        // 获取请求中设置的映射枚举,默认使用DB1
        MappingEnum targetMapping = (MappingEnum) request.getAttribute("DB_MAPPING");
        if (targetMapping == null) {
            targetMapping = MappingEnum.DB1_MAPPING;
        }

        // 校验字段名是否为空
        if (filterDto.getName() == null) {
            ctx.disableDefaultConstraintViolation();
            ctx.buildConstraintViolationWithTemplate("过滤字段名不能为空")
               .addConstraintViolation();
            return false;
        }

        // 获取当前映射对应的字段类型配置
        DataBaseTypeObject typeObject = filterDto.getObjectWithFieldName(targetMapping);
        if (typeObject == null) {
            ctx.disableDefaultConstraintViolation();
            ctx.buildConstraintViolationWithTemplate("无效的过滤字段:" + filterDto.getName())
               .addConstraintViolation();
            return false;
        }

        // 校验值的类型是否匹配
        return typeObject.getType().equals(filterDto.getValue().getClass());
    }
}

步骤4:恢复FilterDto类的注解

保留FilterDto类上的@FilterValidatorBehaviour注解:

@Getter
@Setter
@FilterValidatorBehaviour
public class FilterDto {
    String name;
    String operation;
    String value;

    @JsonIgnore
    public DataBaseTypeObject getObjectWithFieldName(MappingEnum mapping) {
        return mapping.getMappingMap().get(name);
    }
}

步骤5:控制器接口无需修改

控制器接口保持原有写法即可,拦截器会自动处理映射规则的传递:

@PostMapping(path = "/v1/abc")
public PageableDto getAllSomething(
        @RequestAttribute UserClaim userClaim,
        @Valid SearchParametersDto searchParametersDto,
        @RequestBody @Valid List<FilterDto> filters) {
    // 业务逻辑实现
}

@PostMapping(path = "/v1/xyz")
public PageableDto getAllAnotherThing(
        @RequestAttribute UserClaim userClaim,
        @Valid SearchParametersDto searchParametersDto,
        @RequestBody @Valid List<FilterDto> filters) {
    // 业务逻辑实现
}

内容的提问来源于stack exchange,提问作者Sunny Khatik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 11:35:20