我试图使用自定义验证器验证枚举,在我的自定义验证器中,当枚举值中不存在参数时,我试图返回自定义消息。
Bellow my enum
public enum Type {
MISSING_SITE,
INACTIVE_SITE;
}
Bellow myPostMapping
方法
@PostMapping(value = "/line-kpi", produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Find Kpis by one or more customer property")
public ResponseEntity<List<KpiDTO>> findKPILineByCustomer(@RequestBody @ValidCustomerParameter CustomerParameter customerParameter, @RequestParam @ValidExtractionDate String extractionDate) {
var linesKpi = Optional.ofNullable(
kpiService.findKPILineByCustomer(
Optional.ofNullable(customerParameter.getEntityPerimeter()).orElse(List.of()),
Optional.ofNullable(customerParameter.getName()).orElse(List.of()),
Optional.ofNullable(customerParameter.getIc01()).orElse(List.of()),
Optional.ofNullable(customerParameter.getSiren()).orElse(List.of()),
Optional.ofNullable(customerParameter.getEnterpriseId()).orElse(List.of()),
LocalDate.parse(extractionDate)
)
);
return linesKpi.map(ResponseEntity::ok).orElseThrow(() -> new ResourceNotFoundException(KPIS));
}
我不能在方法本身中将枚举的类型切换为字符串,因为我使用的是swagger,它为枚举显示了一个很好的选择列表。
不幸的是,当我尝试为Type提供不同的值时,它会返回一个错误的请求,并且我的验证器不会被触发。
因此,当我的枚举到达控制器时,我试图将其序列化为String,为此,我需要使用Jackson,我试图寻找一个解决方案,但我找不到适合我的情况的解决方案。
Bellow是我的验证器
public class ReportTypeValidator implements ConstraintValidator<ValidReportType, Type> {
private String globalMessage;
@Override
public void initialize(ValidReportType constraintAnnotation) {
ConstraintValidator.super.initialize(constraintAnnotation);
globalMessage = constraintAnnotation.message();
}
@Override
public boolean isValid(Type type, ConstraintValidatorContext constraintValidatorContext) {
if (Arrays.stream(Type.values()).filter(type1 -> type1.equals(type)).toList().isEmpty()) {
constraintValidatorContext
.buildConstraintViolationWithTemplate(globalMessage + ", report type does not exist")
.addConstraintViolation();
return false;
}
return true;
}
}
@Constraint(validatedBy = ReportTypeValidator.class)
@Target( { ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@Valid
public @interface ValidReportType {
String message() default "Invalid value for report type";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
有人能告诉我如何将枚举转换为字符串,以便我的验证器能够处理它吗?
添加一个特殊的枚举常量,指示请求JSON包含无效的枚举常量名称。请求JSON实际上不应该包含此枚举常量的名称。还添加一个方法,Jackson将在反序列化时调用该方法,将JSON字符串转换为枚举常量。如果JSON字符串不是已知的枚举常量名称,则此方法返回特殊的枚举常量。
public enum Type {
MISSING_SITE,
INACTIVE_SITE,
@JsonProperty("SHOULD NEVER ACTUALLY APPEAR IN REQUEST JSON")
INVALID;
/**
* Converts enum constant name to enum constant.
*
* @param name
* enum constant name
* @return enum constant, or {@link #INVALID} if there is no enum constant with that name
*/
@JsonCreator
public static Type valueOfOrInvalid(String name) {
try {
return Type.valueOf(name);
} catch (IllegalArgumentException e) {
return INVALID;
}
}
}
在ReportTypeValidator.isValid(
方法内部,检查枚举常量是否为INVALID
。
if (type == Type.INVALID) {
// Add constraint violation.
我找到了它,我可以通过实现一个新的转换器来实现它,该转换器将字符串转换为有效的枚举值或INVALID值:
public class TypeConverter implements Converter<String, Type> {
@Override
public Type convert(String source) {
if (Arrays.stream(Type.values()).filter(type -> Objects.equals(type.toString(), source)).toList().isEmpty()) {
return Type.INVALID;
}
return Type.valueOf(source.toUpperCase());
}
}
之后,我为我的转换器添加了一个新的配置:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new TypeConverter());
}
}
此外,我不得不通过添加@Schema注释来隐藏枚举的INVALID值:
@Schema(allowableValues = {"MISSING_SITE","INACTIVE_SITE"}, type = "String")
public enum Type {
MISSING_SITE,
INACTIVE_SITE,
INVALID
}
最后,在验证器中,我应该拒绝INVALID值并显示一条自定义消息:
public class ReportTypeValidator implements ConstraintValidator<ValidReportType, Type> {
private String globalMessage;
@Override
public void initialize(ValidReportType constraintAnnotation) {
ConstraintValidator.super.initialize(constraintAnnotation);
globalMessage = constraintAnnotation.message();
}
@Override
public boolean isValid(Type type, ConstraintValidatorContext constraintValidatorContext) {
if (type == Type.INVALID) {
constraintValidatorContext
.buildConstraintViolationWithTemplate(globalMessage)
.addConstraintViolation();
return false;
}
return true;
}
}
上一个验证器的注释:
@Constraint(validatedBy = ReportTypeValidator.class)
@Target( { ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@Valid
public @interface ValidReportType {
String message() default "Invalid value for report type";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}