Проверка JSR-303 в контроллере Spring и получение имени @JsonProperty

Я делаю проверку с JSR-303 в моем приложении Spring оно работает по мере необходимости.

Это пример:

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

И клиенты REST API используют customer_name как имя поля ввода, которое отправляет ошибку поля проверки бутона API org.springframework.validation.FieldError возвращается name как имя поля.

Есть ли какой-нибудь горячий способ получить JSON-ish имя, указанное в @JsonProperty? Или мне нужно реализовать собственный маппер для сопоставления имени поля класса с его альтернативой JSON?

Edit1: переименование полей класса в имена, которые соответствуют именам JSON, не является альтернативой (по многим причинам).

4 ответа

Теперь это можно сделать с помощью PropertyNodeNameProvider.

За MethodArgumentNotValidExceptionа также BindExceptionЯ написал метод, который пытается получить доступ к приватному ConstraintViolationс весны ViolationFieldErrorчерез отражение.

        /**
   * 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);
   ...
}

Вот функция, которая получает значение от@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();
}

Нет способа достичь этого в настоящее время. У нас есть проблема для этого в эталонной реализации: HV-823.

Это решит проблему на стороне Hibernate Validator (т.е. вернет имя, от которого вы ожидаете Path.Node#getName()), потребовалось бы еще проверить, действительно ли Spring выбирает имя оттуда.

Может быть, вы хотели бы помочь в реализации этого?

Другие вопросы по тегам