java Spring boot mvc按值验证枚举
在我的spring boot应用程序中,我想通过自定义值验证enum: 我的数据如下:
@Data
public class PayOrderDTO {
@NotNull
@EnumValidator(enumClass = TransactionMethod.class)
private TransactionMethod method;
}
我的枚举验证器注释定义如下:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
@Constraint(validatedBy = EnumValidatorImpl.class)
public @interface EnumValidator {
String message() default "is invalid";
/**
* @return Specify group
*/
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
/**
* @return Specifies the enumeration type. The parameter value must be a value in this enumeration type
*/
Class<? extends EnumBase> enumClass();
/**
* @return Can it be null
*/
boolean nullable() default false;
/**
* @return Values to exclude
*/
int[] exclusion() default {};
}
这是我的枚举验证器注释的实现
public class EnumValidatorImpl implements ConstraintValidator<EnumValidator, EnumBase> {
private boolean nullable;
private Set<String> values;
@Override
public void initialize(EnumValidator constraintAnnotation) {
this.nullable = constraintAnnotation.nullable();
Class<? extends EnumBase> enumClass = constraintAnnotation.enumClass();
int[] exclusion = constraintAnnotation.exclusion();
values = new HashSet<>();
EnumBase[] enumConstants = enumClass.getEnumConstants();
for (EnumBase iEnum : enumConstants) {
values.add(iEnum.getValue());
}
if (exclusion.length > 0)
for (int i : exclusion) {
values.remove(i);
}
}
@Override
public boolean isValid(EnumBase param, ConstraintValidatorContext constraintValidatorContext) {
if (nullable && param == null) {
return true;
}
else if(param == null)
return false;
return values.contains(param.getValue());
}
}
这是我的枚举:
public enum TransactionMethod implements EnumBase {
CREDIT_CARD("creditcard"),
DEBIT_CARD("debitcard");
public String label;
TransactionMethod(String label) {
this.label = label;
}
@Override
public String getValue() {
return this.label;
}
@JsonCreator(mode = JsonCreator.Mode.DELEGATING)
public static TransactionMethod fromString(String value) {
return TransactionMethod.valueOf(value);
// return Arrays.stream(TransactionMethod.values())
// .filter(el -> el.getValue().equals(value))
// .findFirst()
// .orElseThrow(() -> {
// throw new IllegalArgumentException("Not valid method");
// });
}
}
当我向rest控制器发送http请求时:
@RequiredArgsConstructor
@RestController
@RequestMapping("/orders")
@Validated
public class PaymentRestController {
public ResponseEntity<?> createPayment(
@Valid @RequestBody PayOrderDTO payOrderDTO
) {
return ResponseEntity.ok("Worked");
}
}
请求示例:
POST /orders/ HTTP/1.1
Host: store.test
Content-Type: application/json
Content-Length: 152
{
"method":"creditcard",
}
我希望在我的枚举验证器中得到定义的无效异常或错误消息,而在控制台中得到包含以下内容的异常:
JSON parse error: Cannot construct instance of `x.TransactionMethod`, problem: No enum constant x.TransactionMethod.creditcard
但如果我发出这个请求:
POST /orders/ HTTP/1.1
Host: store.test
Content-Type: application/json
Content-Length: 152
{
"method":"CREDIT_CARD",
}
应用程序工作正常
我想使用标签而不是枚举的常量值来验证枚举,如果它不存在,将抛出一个验证错误,如下所示:
HTTP 422 : field 'method' is not valid, expected values are ['creditcard','debitcard']
我尝试了一些解决方案,比如转换器
public class TransactionMethodStringEnumConverter implements Converter<String, TransactionMethod> {
@Override
public TransactionMethod convert(String source) {
Optional<TransactionMethod> first = Arrays.stream(TransactionMethod.values()).filter(e -> e.label.equals(source)).findFirst();
return first.orElseThrow(() -> {
throw new IllegalArgumentException();
});
}
}
但我好像什么都没做。 如果有人能想出一个很好的解决方案,我将不胜感激,谢谢🙏
# 1 楼答案
要按
label
值反序列化枚举,可以在getter上使用@JsonValue
注释:How To Serialize and Deserialize Enums with Jackson
此外,请注意以下事实:
EnumValidatorImpl
的initialize()
方法中有values.remove(i)
,尽管Set
的元素没有索引,并且Set<String> values
具有泛型String
李>EnumValidator
中,可以使用boolean nullable()
设置nullable=true
,但在PayOrderDTO
中,仍然使用@NotNull
检查method
字段是否为空,这可能会导致不希望的结果李>编辑:
可以使用MessageSource定义特定于语言环境的消息,请参见this article
在你的留言里。属性:
invalid.transaction.method=is not valid, expected values are ['creditcard','debitcard']
然后将消息添加到注释:
通过在
@RestController
或@RestControllerAdvice
中的@ExceptionHandler
方法中捕获MethodArgumentNotValidException
并根据需要格式化消息,例如: