С моей точки зрения, Hibernate Bean Validator
, вероятно, является одним из наиболее удобных методов проверки annotated
полей bean-компонента в любое время и в любом месте. Это как setup
и forget
- Настройте валидатор компонентов Hibernate
- Настройте, как должна выполняться проверка
- Запускать валидатор на bean-компоненте в любом месте
Я выполнил инструкции в документации, приведенной здесь
Установить зависимости
Я использую Gradle, поэтому я собираюсь добавить необходимые зависимости, как показано ниже.
// Hibernate Bean validator
compile('org.hibernate:hibernate-validator:5.2.4.Final')
Создайте универсальный вальдиатор фасоли
Я настраиваю интерфейс валидатора bean-компонентов, как описано в документации, а затем использую его для проверки всего, что аннотировано.
public interface CustomBeanValidator {
/**
* Validate all annotated fields of a DTO object and collect all the validation and then throw them all at once.
*
* @param object
*/
public <T> void validateFields(T object);
}
Реализуйте вышеуказанный интерфейс следующим образом
@Component
public class CustomBeanValidatorImpl implements CustomBeanValidator {
ValidatorFactory valdiatorFactory = null;
public CustomBeanValidatorImpl() {
valdiatorFactory = Validation.buildDefaultValidatorFactory();
}
@Override
public <T> void validateFields(T object) throws ValidationsFatalException {
Validator validator = valdiatorFactory.getValidator();
Set<ConstraintViolation<T>> failedValidations = validator.validate(object);
if (!failedValidations.isEmpty()) {
List<String> allErrors = failedValidations.stream().map(failure -> failure.getMessage())
.collect(Collectors.toList());
throw new ValidationsFatalException("Validation failure; Invalid request.", allErrors);
}
}
}
Класс исключения
ValidationsFatalException
, который я использовал выше, является настраиваемым классом исключений, расширяющим RuntimeException
. Как видите, я передаю сообщение и список violations
на случай, если DTO имеет более одной ошибки проверки.
public class ValidationsFatalException extends RuntimeException {
private String message;
private Throwable cause;
private List<String> details;
public ValidationsFatalException(String message, Throwable cause) {
super(message, cause);
}
public ValidationsFatalException(String message, Throwable cause, List<String> details) {
super(message, cause);
this.details = details;
}
public List<String> getDetails() {
return details;
}
}
Моделирование вашего сценария
Чтобы проверить, работает это или нет, я буквально использовал ваш код для тестирования, и вот что я сделал
- Создайте конечную точку, как показано выше
- Автоматически подключите
CustomBeanValidator
и активируйте его validateFields
метод, передав ему productRequest
, как показано ниже.
- Создайте класс
ProductRequest
, как показано выше
- Я аннотировал
productId
@NotNull
и @Length(min=5, max=10)
- Я использовал
Postman
, чтобы сделать GET
запрос с params
, имеющим значение url-encoded
тело json
Предполагая, что CustomBeanValidator
автоматически подключается к контроллеру, запустите проверку, как показано ниже, после создания объекта productRequest
.
beanValidator.validateFields(productRequest);
Вышеупомянутое вызовет исключение, если какие-либо нарушения на основе использованных аннотаций.
Как исключение обрабатывается контроллером исключений?
Как упоминалось в заголовке, я использую ExceptionController
для обработки исключений в моем приложении.
Вот как скелет моего exception handler
, где ValidationsFatalException
отображается, а затем я обновляю сообщение и устанавливаю желаемый код состояния на основе типа исключения и возвращаю настраиваемый объект (то есть json, который вы видите ниже)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler({SomeOtherException.class, ValidationsFatalException.class})
public @ResponseBody Object handleBadRequestExpection(HttpServletRequest req, Exception ex) {
if(ex instanceof CustomBadRequestException)
return new CustomResponse(400, HttpStatus.BAD_REQUEST, ex.getMessage());
else
return new DetailedCustomResponse(400, HttpStatus.BAD_REQUEST, ex.getMessage(),((ValidationsFatalException) ex).getDetails());
}
Тест 1
Необработанный params = {"productId":"abc123"}
URL в кодировке parmas = %7B%22productId%22%3A%22abc123%22%7D
Конечный URL: http://localhost:8080/app/product?params=%7B%22productId%22%3A%22abc123%22%7D
Результат: все в порядке.
Тест 2
Необработанный params = {"productId":"ab"}
URL в кодировке parmas = %7B%22productId%22%3A%22ab%22%7D
Конечный URL: http://localhost:8080/app/product?params=%7B%22productId%22%3A%22ab%22%7D
Результат:
{
"statusCode": 400,
"status": "BAD_REQUEST",
"message": "Validation failure; Invalid request.",
"details": [
"length must be between 5 and 10"
]
}
Вы можете расширить реализацию Validator
, чтобы отобразить сообщение об ошибке field vs message
.
person
Raf
schedule
20.02.2018
productRequest
с вашими любимыми аннотациями валидатора спящего режима, прежде чем выполнять какие-либо действия с ним. - person Raf   schedule 20.02.2018