1. Обзор
В этом кратком руководстве мы сосредоточимся на различиях между @Valid и @Validated аннотациями весной.
Проверка входных данных пользователей является обычной функцией в большинстве наших приложений. В экосистеме Java мы специально используем Java Standard Bean Validation API для поддержки этого. Кроме того, это также хорошо интегрировано с Spring начиная с версии 4.0. |/@Valid и @Validated Аннотации вытекают из этого стандартного API Bean .
В следующих разделах давайте рассмотрим их подробно.
2. @Действительные и @Проверенные аннотации
Весной мы используем аннотацию JSR-303 @Valid для проверки уровня метода . Кроме того, мы также используем его для пометки атрибута члена для проверки . Однако эта аннотация не поддерживает групповую проверку.
Группы помогают ограничить ограничения, применяемые во время проверки. Одним из конкретных вариантов использования являются мастера пользовательского интерфейса. Здесь, на первом шаге, у нас может быть определенная подгруппа полей. На следующем этапе может быть другая группа, принадлежащая к тому же бобу. Следовательно, нам нужно применять ограничения к этим ограниченным полям на каждом шаге, но @Valid не поддерживает это.
В этом случае для группового уровня мы должны использовать Spring @Validated, который является вариантом этого JSR-303 @Valid . Это используется на уровне метода. А для маркировки атрибутов членов мы продолжаем использовать аннотацию @Valid .
Теперь давайте сразу погрузимся и рассмотрим использование этих аннотаций на примере.
3. Пример
Давайте рассмотрим простую форму регистрации пользователя, разработанную с использованием Spring Boot. Для начала у нас будут только атрибуты name и password :
public class UserAccount { @NotNull @Size(min = 4, max = 15) private String password; @NotBlank private String name; // standard constructors / setters / getters / toString }
Далее давайте посмотрим на контроллер. Здесь у нас будет метод save Basic Info с аннотацией @Valid для проверки пользовательского ввода:
@RequestMapping(value = "/saveBasicInfo", method = RequestMethod.POST) public String saveBasicInfo( @Valid @ModelAttribute("useraccount") UserAccount useraccount, BindingResult result, ModelMap model) { if (result.hasErrors()) { return "error"; } return "success"; }
Теперь давайте проверим этот метод:
@Test public void givenSaveBasicInfo_whenCorrectInput_thenSuccess() throws Exception { this.mockMvc.perform(MockMvcRequestBuilders.post("/saveBasicInfo") .accept(MediaType.TEXT_HTML) .param("name", "test123") .param("password", "pass")) .andExpect(view().name("success")) .andExpect(status().isOk()) .andDo(print()); }
После подтверждения успешного выполнения теста давайте расширим функциональность. Следующим логическим шагом является преобразование этой формы в многоступенчатую регистрационную форму, как это происходит с большинством мастеров. Первый шаг с именем и паролем остается неизменным. На втором шаге мы получим дополнительную информацию, такую как возраст и телефон . Поэтому мы обновим наш объект домена этими дополнительными полями:
public class UserAccount { @NotNull @Size(min = 4, max = 15) private String password; @NotBlank private String name; @Min(value = 18, message = "Age should not be less than 18") private int age; @NotBlank private String phone; // standard constructors / setters / getters / toString }
Однако на этот раз мы заметим, что предыдущий тест не удался. Это связано с тем, что мы не передаем поля возраст и телефон , которых все еще нет на картинке в пользовательском интерфейсе . Для поддержки такого поведения нам понадобится групповая проверка и аннотация @Validated .
Для этого нам нужно сгруппировать поля, создав две отдельные группы. Во-первых, нам нужно будет создать два маркерных интерфейса. Отдельный для каждой группы или каждого шага. Мы можем обратиться к нашей статье о групповой проверке для точной реализации этого. Здесь давайте сосредоточимся на различиях в аннотациях.
У нас будет Базовая информация интерфейс для первого шага и Предварительная информация для второго шага. Кроме того, мы обновим наш класс UserAccount , чтобы использовать эти интерфейсы маркеров следующим образом:
public class UserAccount { @NotNull(groups = BasicInfo.class) @Size(min = 4, max = 15, groups = BasicInfo.class) private String password; @NotBlank(groups = BasicInfo.class) private String name; @Min(value = 18, message = "Age should not be less than 18", groups = AdvanceInfo.class) private int age; @NotBlank(groups = AdvanceInfo.class) private String phone; // standard constructors / setters / getters / toString }
Кроме того, теперь мы обновим наш контроллер, чтобы использовать аннотацию @Validated вместо @Valid :
@RequestMapping(value = "/saveBasicInfoStep1", method = RequestMethod.POST) public String saveBasicInfoStep1( @Validated(BasicInfo.class) @ModelAttribute("useraccount") UserAccount useraccount, BindingResult result, ModelMap model) { if (result.hasErrors()) { return "error"; } return "success"; }
В результате этого обновления наш тест теперь успешно выполняется. Теперь давайте также протестируем этот новый метод:
@Test public void givenSaveBasicInfoStep1_whenCorrectInput_thenSuccess() throws Exception { this.mockMvc.perform(MockMvcRequestBuilders.post("/saveBasicInfoStep1") .accept(MediaType.TEXT_HTML) .param("name", "test123") .param("password", "pass")) .andExpect(view().name("success")) .andExpect(status().isOk()) .andDo(print()); }
Это тоже работает успешно. Следовательно, мы можем видеть, как использование @Validated имеет важное значение для групповой проверки.
Далее давайте посмотрим, как @Valid необходим для запуска проверки вложенных атрибутов.
4. Использование аннотации @Valid для обозначения вложенных объектов
Аннотация @Valid используется для обозначения вложенных атрибутов, в частности . Это запускает проверку вложенного объекта. Например, в нашем текущем сценарии давайте создадим Адрес пользователя объект:
public class UserAddress { @NotBlank private String countryCode; // standard constructors / setters / getters / toString }
Чтобы обеспечить проверку этого вложенного объекта, мы украсим атрибут аннотацией @Valid :
public class UserAccount { //... @Valid @NotNull(groups = AdvanceInfo.class) private UserAddress useraddress; // standard constructors / setters / getters / toString }
5. Плюсы и минусы
Давайте рассмотрим некоторые плюсы и минусы использования @Valid и @Validated аннотаций весной.
Аннотация @Valid обеспечивает проверку всего объекта. Важно отметить, что он выполняет проверку всех графов объектов. Однако это создает проблемы для сценариев, требующих только частичной проверки.
С другой стороны, мы можем использовать @Validated для групповой проверки, включая приведенную выше частичную проверку. Однако в этом случае проверяемые сущности должны знать правила проверки для всех групп или вариантов использования, в которых они используются. Здесь мы смешиваем проблемы, следовательно, это может привести к анти-паттерну.
6. Заключение
В этом кратком руководстве мы рассмотрели ключевые различия между @Valid и @Validated аннотациями.
В заключение, для любой базовой проверки мы будем использовать аннотацию JSR @Valid в наших вызовах методов. С другой стороны, для любой проверки группы, включая групповые последовательности , нам нужно будет использовать аннотацию Spring @Validated в нашем вызове метода. Аннотация @Valid также необходима для запуска проверки вложенных свойств.
Как всегда, код, представленный в этой статье, доступен на GitHub .