【问题标题】:How to validate enum in DTO?如何验证 DTO 中的枚举?
【发布时间】:2020-06-14 12:07:28
【问题描述】:

在域模型对象中,我有以下字段:

private TermStatus termStatus;

TermStatus 是一个枚举:

public enum TermStatus {
    NONE,
    SUCCESS,
    FAIL
}

在 DTO 中,我的字段与域对象中的字段相同。问题是,如何验证传递的值?如果 API 客户端现在将带有枚举值的错误字符串作为参数传递(例如,nOnE),它将不会收到有关错误的任何信息,只会收到状态 400 Bad Request。是否可以像这样验证它,例如,在javax.validation 注释的情况下,如@NotBlank@Size,如果出现错误,至少会清楚它是什么。有一个想法为此进行单独的映射,例如"items/1/complete-term" 而不是直接枚举传输,因此在这种情况下,服务器本身会将SUCCESS 值设置为termStatus 字段。但据我所知,这些东西在 REST API 中看起来不太好,所以我需要你的想法

【问题讨论】:

    标签: java spring spring-boot rest enums


    【解决方案1】:

    听起来您需要在验证后实现自己的响应,并告诉 API 客户端您收到的 DTO 中的数据无效,并返回带有实际收到值(在您的情况下为 nOnE)的消息,也许还有您的有效值列表(如果这不会成为安全问题)。另外,我认为您响应的理想 http 状态应该是 422 而不是通用的 400 Bad Request。

    对于您的实际验证实施,我认为您可以直接将您从 API 客户端收到的数据从 DTO 转换为 ENUM 的值与后端的 ENUM 值进行比较。如果等于任何 ENUM 值,则它是有效请求 (200),否则为 422。

    希望这会有所帮助!

    【讨论】:

      【解决方案2】:

      您可以在enum 中创建一个实用方法,如下所示 私有字符串文本;

      TermStatus(String text) {
          this.text = text;
      }
      public static TermStatus fromText(String text) {
              return Arrays.stream(values())
                      .filter(bl -> bl.text.equalsIgnoreCase(text))
                      .findFirst()
                      .orElse(null);
          }
      

      并在dto 中设置值,如下所示

      dto.setTermStatus(TermStatus.fromText(passedValue))
      if(dto.getTermStatus()== null)
      throw new Exception("Your message");
      

      希望这会有所帮助!

      【讨论】:

        【解决方案3】:

        您可以检查 String 是否对特定枚举有效,而不是直接验证枚举。要实现这样的效果,您可以创建自己的枚举验证注解。

        @Documented
        @Constraint(validatedBy = EnumValidatorConstraint.class)
        @Retention(RetentionPolicy.RUNTIME)
        @Target(ElementType.FIELD)
        @NotNull
        public @interface EnumValidator {
        
        Class<? extends Enum<?>> enum();
        String message() default "must be any of enum {enum}";
        Class<?>[] groups() default {};
        Class<? extends Payload>[] payload() default {};
        }
        

        然后你需要实现验证器来检查字符串是否作为这个枚举的一部分存在。

        public class EnumValidatorConstraint implements ConstraintValidator<EnumValidator, String> {
        
        Set<String> values;
        
        @Override
        public void initialize(EnumValidator constraintAnnotation) {
            values = Stream.of(constraintAnnotation.enumClass().getEnumConstants())
                    .map(Enum::name)
                    .collect(Collectors.toSet());
        }
        
        @Override
        public boolean isValid(String value, ConstraintValidatorContext context) {
            return values.contains(value);
        }
        }
        

        最后,您需要使用 @EnumValidator 注释您的字段。

        @EnumValidator(enum = TermStatus.class)
        private String termStatus;
        

        如果字符串不匹配,将抛出 MethodArgumentNotValidException。与@NotNull 或其他约束验证相同。

        【讨论】:

          【解决方案4】:

          您应该为termStatus 使用字符串数据类型。因为客户端为此发送字符串值。然后您必须创建自定义验证约束来修复此问题,如下所示。

          ValueOfEnum

          @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
          @Retention(RUNTIME)
          @Documented
          @Constraint(validatedBy = ValueOfEnumValidator.class)
          public @interface ValueOfEnum
          {
              Class<? extends Enum<?>> enumClass();
          
              String message() default "";
          
              Class<?>[] groups() default {};
          
              Class<? extends Payload>[] payload() default {};
          }
          

          ValueOfEnumValidator

          public class ValueOfEnumValidator implements ConstraintValidator<ValueOfEnum, CharSequence>
          {
              private List<String> acceptedValues;
          
              @Override
              public void initialize(ValueOfEnum annotation)
              {
                  acceptedValues = Stream.of(annotation.enumClass().getEnumConstants())
                      .map(Enum::name)
                      .collect(Collectors.toList());
              }
          
              @Override
              public boolean isValid(CharSequence value, ConstraintValidatorContext context)
              {
                  if (value == null) {
                      return true;
                  }
                  return acceptedValues.contains(value.toString());
              }
          }
          

          现在您可以为您的域模型添加@ValueOfEnum 注释。然后在你的控制器类域对象前面添加@Validated注解。

          @ValueOfEnum(enumClass = TermStatus.class, message = "Invalid Term Status")
          private String termStatus;
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2020-05-07
            • 1970-01-01
            • 2019-08-17
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-09-06
            • 2012-07-01
            相关资源
            最近更新 更多