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 .