Рубрики
Без рубрики

Различия в аннотациях @Valid и @Validated весной

Изучите ключевые различия между аннотациями @Valid и @Validated.

Автор оригинала: baeldung.

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 .