2012-07-23

Еще немного о валидации

В предыдущей статье мы показали, каким образом можно организовать валидацию данных, поступающих на наши контроллеры. Сегодня я бы хотел рассказать про дополнительные возможности, которые предоставляет Spring. В часности, в этой стетье пойдет речь о JSR-303 (Валидация Бинов) и Hibernate Validation. Также я расскажу о новшествах, которые были введены в Spring MVC 3.1 для более удобной валидации данных.

Итак, построим наши примеры на предыдущей бизнес задаче: создание скидочных купонов. На вход наш контроллер получает данные о купонах, которые нужно сгенерить (DiscountCouponGenerationCommand), в качестве результата возвращает список сгенерированных купонов (CouponJson) либо список ошибок валидации.

Поля нашей команды помечены специальными аннотациями, которые называются Constraint. В примере я использовал несколько стандартных аннотаций @Min, @Digits. Также я использовал свои собвстенные Constraint аннотации: @CouponCode для валидации поля couponCode, @DiscountCoupon для валидации всего бина DiscountCouponGenerationCommand.

Как же работают Constraint аннотации? Давайте заглянем внутрь одной их них:


Прежде всего, обратите внимание на аннотацию @Constraint, которая указывает на то, что @CouponCode является Constraint аннотацией, также она содержит в себе имя валидатора, который будет использоваться для валидации поля, помеченного как @CouponCode. В нашем случае валидирующий класс - это CouponCodeValidator. Из других частей аннотации также следует обратить внимание на параметр message, который содержит сообщение-ошибку, которое будет передано на клиент, в случае если валидация поля прошла не успешно. Также аннотация @Target содержит список элементов Java класса, перед которыми мы сможем использовать @CouponCode.

Следует сказать, что текст ошибок валидации можно указывать явно в классе, либо использовать property файл, что особенно удобно, если необходима локализация сообщений. В этом случае вместо сообщения в параметре message нужно указать ключ окруженный фигурными скобками, а само сообщение поместить в файл ValidationMessages.properties, который обязательно должен находиться в корне classpath:

Рассмотрим теперь сам валидатор CouponCodeValidator.

Как видно из примера, валидатор имплементирует generic интерфейс. Первый generic параметр - это constraint который обрабатывает валидатор, второй - тип поля, на котором предположительно будет использоваться constraint. Итак, наш валидатор проверяет, что поле анотированное @CouponCode не пустое, имеет длину 10 символов и начинается с символов 'CP'.

Теперь перейдем собственно к конроллеру, который будет заниматься обработкой команды, валидацией и генерацией купонов.

Мы помечаем параметр DiscountCouponGenerationCommand аннотацией @Valid, которая и включит механизм валидации перед тем, как будет запущен метод generateDiscountCoupons. Всю работу за нас сделает Spring MVC:

  • вначале будет создана команда DiscountCouponGenerationCommand, которая заполнится значениями из POST запроса;
  • далее команду просканируют на наличие constraint аннотаций, и для каждой из них запустится соответствующий валидатор;
  • если ошибок не будет, то вызовется метод контроллера generateDiscountCoupons.
А что будет, если ошибки валидации все же возникнут? В более старых версиях Spring MVC для обработки ошибок использовался специальный параметр BindingResult, который автоматически подставлялся в handler метод контроллера. Но использовать его было достаточно не удобно, так как методы контроллера фактически копировали один и тот же код, обрабатывая ошибки валидации. В предыдущей статье эту проблему попытались решить с помощью аспекта. Способ хороший, но не достаточно прозрачный.
В последних версиях Spring MVC ввели еще одну возможность обработки ошибок валидации - с помощью обработчиков исключений.

Метод аннотированный @ExceptionHandler аннотацией будет автоматически использоваться для обработки ошибок валидации, если таковые возникнут. Параметр MethodArgumentNotValidException содержит всю необходимую нам информацию об ошибках и месте где они возникли.

Конфигурация Spring контекста для JSR-303 не требует ничего особенного, она содержит 2 строки, которые инициализируют поиск компонент и Spring MVC:

В качестве имплементации JSR-303 я использовал Hibernate Validator, его необходимо поместить в classpath. Так как для сборки я использую Maven, я включил в pom.xml следующие зависимости:

Также хочу сказать, что весь код, использованный в примерах, находится на GitHub: https://github.com/hoaz/spring-validation-example. Вы можете склонировать себе этот репозиторий, собрать и поэксперементировать с примерами.

No comments:

Post a Comment