Я делаю проверку в своем классе Spring REST Controller. Мой пользовательский валидатор ограничений не выполняется, если нарушение самого ограничения происходит с самим объектом аргумента.
Ожидание
Запрос:
{
fieldA: "2020-01-05",
fieldB: "2020-01-02",
fieldC: ""
}
Ответ:
{
errors: [
"Field C is mandatory",
"Invalid date range"
]
}
В настоящее время - сценарий 1
Запрос:
{
fieldA: "2020-01-05",
fieldB: "2020-01-02",
fieldC: ""
}
Ответ:
{
errors: [
"Field C is mandatory"
]
}
В настоящее время - сценарий 2
Запрос:
{
fieldA: "2020-01-05",
fieldB: "2020-01-02",
fieldC: "<some-value>"
}
Ответ:
{
errors: [
"Invalid date range."
]
}
Вот фрагмент кода
SomeAPI . java
@RestController
@RequestMapping(value = "/")
@Validated
public class SomeAPI {
@PostMapping
@SomeValidation
public ResponseEntity<Map<String, Object>> saveSomething(@Valid @RequestBody SomeForm form) {
return new ResponseEntity<>(responseBody, HttpStatus.CREATED);
}
}
SomeForm. java
public class SomeForm {
@NotNull(message = "Field A is mandatory")
private LocalDate fieldA;
@NotNull(message = "Field B is mandatory")
private LocalDate fieldB;
@NotBlank(message = "Field C is mandatory")
private String fieldC;
// Setters and getters
}
SomeValidation. java
@Documented
@Constraint(validatedBy = SomeValidator.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })
public @interface SomeValidation {
String message() default "";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
@SupportedValidationTarget(ValidationTarget.PARAMETERS)
class SomeValidator implements ConstraintValidator<SomeValidation, Object[]> {
private static Logger logger = LoggerFactory.getLogger(SomeValidator.class);
@Override
public boolean isValid(Object[] value, ConstraintValidatorContext context) {
context.disableDefaultConstraintViolation();
SomeForm form = (SomeForm) value[0];
boolean valid = true;
if (!isValid(form)) {
valid = false;
context.buildConstraintViolationWithTemplate("Invalid date range").addConstraintViolation();
}
return valid;
}
private boolean isValid(SomeForm form) {
logger.info("isValid");
return form.getFieldA().isBefore(form.getFieldB());
}
}
APIExceptionHandler. java
@ControllerAdvice
public class APIExceptionHandler extends ResponseEntityExceptionHandler {
private static final String TIMESTAMP_KEY = "timestamp";
private static final String STATUS_KEY = "status";
private static final String ERRORS_KEY = "errors";
@Override
protected ResponseEntity<Object> handleBindException(BindException ex, HttpHeaders headers, HttpStatus status,
WebRequest request) {
Map<String, Object> body = new LinkedHashMap<>();
body.put(TIMESTAMP_KEY, new Date());
body.put(STATUS_KEY, status.value());
List<String> errors = ex.getBindingResult().getFieldErrors().stream().map(FieldError::getDefaultMessage)
.collect(Collectors.toList());
body.put(ERRORS_KEY, errors);
return new ResponseEntity<>(body, headers, status);
}
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers,
HttpStatus status, WebRequest request) {
Map<String, Object> body = new LinkedHashMap<>();
body.put(TIMESTAMP_KEY, new Date());
body.put(STATUS_KEY, status.value());
List<String> errors = ex.getBindingResult().getAllErrors().stream().map(ObjectError::getDefaultMessage)
.collect(Collectors.toList());
body.put(ERRORS_KEY, errors);
return new ResponseEntity<>(body, headers, status);
}
@ExceptionHandler({ ConstraintViolationException.class })
public ResponseEntity<Object> handleConstraintViolation(final ConstraintViolationException ex,
final WebRequest request) {
Map<String, Object> body = new LinkedHashMap<>();
body.put(TIMESTAMP_KEY, new Date());
body.put(STATUS_KEY, HttpStatus.BAD_REQUEST.value());
List<String> errors = ex.getConstraintViolations().stream().map(ConstraintViolation::getMessage)
.collect(Collectors.toList());
body.put(ERRORS_KEY, errors);
return new ResponseEntity<>(body, new HttpHeaders(), HttpStatus.BAD_REQUEST);
}
}