SpringBoot自定义校验器报错HV000030:找不到String类型的校验器
问题描述
SpringBoot应用返回UNMAPPED_ERROR,具体报错:
"code": "UNMAPPED_ERROR", "message": "Request processing failed: jakarta.validation.UnexpectedTypeException: HV000030: No validator could be found for constraint .... validating type 'java.lang.String'. Check configuration for 'channel'"
相关代码片段:
SessionInfo的channel字段
@NotBlank(message = "channel is required") @ChannelValidationConstraint @JsonProperty("channel") private String channel;
ChannelType枚举
public enum ChannelType { // allowed values for "channel" field in the SessionInfo payload Mobile("mobile"), Online("online"); private final String fieldName; private static final Map<String, ChannelType> lookup = new HashMap<>(); static { for (ChannelType channelType : EnumSet.allOf(ChannelType.class)) { lookup.put(channelType.getFieldName(), channelType); } } ChannelType(String fieldName) { this.fieldName = fieldName; } public String getFieldName() { return fieldName; } public static ChannelType getByFieldName(String fieldName) { return lookup.get(fieldName); } }
ChannelValidator校验器
public class ChannelValidator implements ConstraintValidator<ChannelValidationConstraint, List<IRequestPayload>> { @Override public boolean isValid(List<IRequestPayload> value, ConstraintValidatorContext context) { SessionInfo sessionInfo = null; for (IRequestPayload requestPayload : value) { if (requestPayload instanceof SessionInfo) { sessionInfo = (SessionInfo) requestPayload; } } if (sessionInfo == null) { context.disableDefaultConstraintViolation(); context.buildConstraintViolationWithTemplate("Invalid 'handler' in request payload") .addConstraintViolation(); return false; } ChannelType channelType = ChannelType.getByFieldName(sessionInfo.getChannel()); switch (channelType) { case Online: return true; case Mobile: return true; default: if (StringUtils.isBlank(channelType.toString())) { context.disableDefaultConstraintViolation(); context.buildConstraintViolationWithTemplate("channel is required").addConstraintViolation(); return false; } else if (channelType.toString().equals(ChannelType.Online.toString()) || !channelType.toString().equals(ChannelType.Mobile.toString())) { context.disableDefaultConstraintViolation(); context.buildConstraintViolationWithTemplate("invalid channel").addConstraintViolation(); return false; } } return true; } }
ChannelValidationConstraint注解
@Documented @Constraint(validatedBy = {ChannelValidator.class}) @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface ChannelValidationConstraint { String message() default ""; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
解决方法
核心问题:校验器与标注字段的类型不匹配。@ChannelValidationConstraint标注在String类型的字段上,但ChannelValidator实现的是针对List<IRequestPayload>类型的校验逻辑,Validator框架无法找到适配String的校验器,因此抛出异常。
按以下步骤修复:
1. 修改ChannelValidator的泛型与逻辑
将校验器的泛型改为适配String类型,同时简化校验逻辑:
public class ChannelValidator implements ConstraintValidator<ChannelValidationConstraint, String> { @Override public boolean isValid(String channelValue, ConstraintValidatorContext context) { // 空值校验可依赖@NotBlank,这里做兜底或自定义提示 if (StringUtils.isBlank(channelValue)) { context.disableDefaultConstraintViolation(); context.buildConstraintViolationWithTemplate("channel is required") .addConstraintViolation(); return false; } // 根据传入的字符串查找枚举 ChannelType channelType = ChannelType.getByFieldName(channelValue); if (channelType == null) { context.disableDefaultConstraintViolation(); context.buildConstraintViolationWithTemplate("invalid channel") .addConstraintViolation(); return false; } return true; } }
原代码中遍历List<IRequestPayload>查找SessionInfo的逻辑完全冗余,现在直接校验字段的String值即可。
2. 保留注解的Target配置
@ChannelValidationConstraint的@Target({ElementType.FIELD})配置正确,无需修改,确保注解可以标注在字段上。
3. 验证修复结果
重启应用后:
- 当channel字段为
mobile或online时,校验通过; - 当channel为空或其他非法值时,会返回对应的自定义错误信息,不再出现
UNMAPPED_ERROR。
内容的提问来源于stack exchange,提问作者ResourceReaper
相关产品推荐
相关产品推荐

