Spring 控制器中的 JSR-303 验证并获取@JsonProperty名称

JSR-303 validation in Spring controller and getting @JsonProperty name

提问人:jnemecz 提问时间:1/18/2017 更新时间:8/23/2023 访问量:3474

问:

我在我的 Spring 应用程序中进行验证,它根据需要工作。JSR-303

下面是一个示例:

@Column(nullable = false, name = "name")
    @JsonProperty("customer_name")
    @NotEmpty
    @Size(min = 3, max = 32)
    private String name;

REST API 客户端用作发送到 API bud 验证字段错误返回的输入字段的名称作为字段名称。customer_nameorg.springframework.validation.FieldErrorname

有没有办法热得到指定的名字?或者我是否必须实现自己的映射器才能将类字段名称映射到其 JSON 替代方案中?JSON-ish@JsonProperty

编辑 1:将类字段重命名为与 JSON 名称相对应的名称不是替代方法(出于多种原因)。

Java 验证 映射 bean-validation

评论

0赞 cassiomolin 8/24/2017
答案可能会为您提供有关如何分析约束冲突,然后使用 Jackson 查找实际 JSON 属性名称的一些见解。

答:

0赞 Gunnar 1/26/2017 #1

目前没有办法做到这一点。我们在参考实现中遇到了这个问题:HV-823

这将解决 Hibernate Validator 方面的问题(即返回您期望的名称),它需要更多的检查 Spring 是否真的从那里获取名称。Path.Node#getName()

也许你有兴趣帮助实现这个?

评论

0赞 cassiomolin 8/24/2017
不久前,我写了这个答案。它是关于如何解析约束冲突,然后使用 Jackson 查找实际的 JSON 属性名称。
2赞 p3consulting 5/16/2018
Spring 从 中获取了 “字段” 名称,并且根据 Javadoc: .默认情况下,在 Spring 应用程序中,实际实例是子类。SpringValidatorAdapter.determineField(ConstraintViolation<Object> violation)The default implementation returns the stringified property pathSpringValidatorAdapterLocalValidatorFactoryBean
5赞 Damir Alibegovic 12/29/2019 #2

现在可以使用 PropertyNodeNameProvider 完成此操作。

评论

0赞 M. Justin 3/16/2021
从我最初的实验来看,这适用于自定义 bean 验证属性名称,但当 Spring 将其转换为 时失败,因为它需要实际的字段名称而不是 JSON 属性名称。ConstraintValidationFieldError
0赞 Damir Alibegovic 3/17/2021
@M.贾斯汀:这是真的。我打开了一个问题:为此 github.com/spring-projects/spring-framework/issues/24811,但还没有回应。解决方法是扩展 ,并覆盖以返回空字符串。这只有在您以后(在某些@ControllerAdvice)创建自己的错误表示时才有效,并且不依赖于 Spring 提供的错误表示。org.springframework.validation.beanvalidation.LocalValidatorFactoryBeangetRejectedValue
0赞 Hollerweger 6/2/2022
对于 FieldErrors,我发现的唯一解决方案是通过反射从 Spring ViolationFieldError 访问私有 ConstraintViolation。请参阅我添加的答案。
1赞 Hollerweger 6/2/2022 #3

因为我编写了一个方法,试图通过反射从 Spring 访问私有。MethodArgumentNotValidExceptionBindExceptionConstraintViolationViolationFieldError

  /**
   * Try to get the @JsonProperty annotation value from the field. If not present then the
   * fieldError.getField() is returned.
   * @param fieldError {@link FieldError}
   * @return fieldName
   */
  private String getJsonFieldName(final FieldError fieldError) {
    try {
      final Field violation = fieldError.getClass().getDeclaredField("violation");
      violation.setAccessible(true);
      var constraintViolation = (ConstraintViolation) violation.get(fieldError);
      final Field declaredField = constraintViolation.getRootBeanClass()
          .getDeclaredField(fieldError.getField());
      final JsonProperty annotation = declaredField.getAnnotation(JsonProperty.class);
      //Check if JsonProperty annotation is present and if value is set
      if (annotation != null && annotation.value() != null && !annotation.value().isEmpty()) {
        return annotation.value();
      } else {
        return fieldError.getField();
      }
    } catch (Exception e) {
      return fieldError.getField();
    }
  }

此代码可用于处理类中 BindExceptions 的方法:@ExceptionHandler(BindException.class)@ControllerAdvice

@ControllerAdvice
public class ControllerExceptionHandler {

  @ExceptionHandler(BindException.class)
  public ResponseEntity<YourErrorResultModel> handleBindException(final BindException exception) {
    for (FieldError fieldError : exception.getBindingResult().getFieldErrors()) {
      final String fieldName = getJsonFieldName(fieldError);
   ...
}

评论

0赞 rem 12/2/2022
你不必使用内省来获得:只需使用violationif (fieldError.contains(ConstraintViolation.class) { var violation = fieldError.unwrap(ConstraintViolation.class); ...}
0赞 Chhaileng 8/23/2023 #4

这是从注解中获取值的函数。@JsonProperty

private String getJsonPropertyValue(final FieldError error) {
    try {
        if (error.contains(ConstraintViolation.class)) {
            final ConstraintViolation<?> violation = error.unwrap(ConstraintViolation.class);
            final Field declaredField = violation.getRootBeanClass().getDeclaredField(error.getField());
            final JsonProperty annotation = declaredField.getAnnotation(JsonProperty.class);

            if (annotation != null && annotation.value() != null && !annotation.value().isEmpty()) {
                return annotation.value();
            }
        }
    } catch (Exception ignored) {
    }

    return error.getField();
}

然后在异常处理程序中

@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ResponseEntity<?> validationExceptionHandler(MethodArgumentNotValidException e) {
    final Map<String, String> errors = new HashMap<>();
    e.getBindingResult().getAllErrors().forEach((error) -> {
        String fieldName = getJsonPropertyValue((FieldError) error);
        String errorMessage = error.getDefaultMessage();
        errors.put(fieldName, errorMessage);
    });
    System.out.println(errors); // put this in your response
    return ResponseEntity.badRequest().build();
}