Проверка 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 ответа
За
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 выбирает имя оттуда.
Может быть, вы хотели бы помочь в реализации этого?