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

Проверка в весенней загрузке

Узнайте, как проверить объекты домена при весенней загрузке с помощью Hibernate Validator, эталонной реализации платформы проверки компонентов.

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

1. Обзор

Когда дело доходит до проверки пользовательского ввода, Spring Boot обеспечивает мощную поддержку этой распространенной, но важной задачи прямо из коробки.

Хотя Spring Boot поддерживает бесшовную интеграцию с пользовательскими валидаторами, фактическим стандартом для выполнения валидации является Hibernate Validator , эталонная реализация Bean Validation framework.

В этом уроке мы рассмотрим, как проверить объекты домена в Spring Boot .

Дальнейшее чтение:

Источник настраиваемого сообщения проверки в весенней загрузке

Разница между ограничениями @NotNull, @NotEmpty и @NotBlank при проверке компонентов

2. Зависимости Maven

В этом случае мы узнаем, как проверить объекты домена в Spring Boot , создав базовый контроллер СБРОСА.

Контроллер сначала примет объект домена, затем проверит его с помощью валидатора Hibernate и, наконец, сохранит его в базе данных H2 в памяти.

Зависимости проекта довольно стандартны:


    org.springframework.boot
    spring-boot-starter-web

 
    org.springframework.boot
    spring-boot-starter-data-jpa
 
 
    com.h2database 
    h2
    1.4.197 
    runtime

Как показано выше, мы включили spring-boot-starter-web в ваш pom.xml файл, потому что он нам понадобится для создания контроллера REST. Кроме того, давайте обязательно проверим последние версии spring-boot-starter-jpa и базу данных H2 на Maven Central.

Начиная с Boot 2.3, нам также необходимо явно добавить зависимость spring-boot-starter-validation :

 
    org.springframework.boot 
    spring-boot-starter-validation 

3. Простой Доменный Класс

Поскольку зависимости вашего проекта уже существуют, далее нам нужно определить пример класса сущностей JPA, роль которого будет заключаться исключительно в моделировании пользователей.

Давайте взглянем на этот класс:

@Entity
public class User {
    
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;
    
    @NotBlank(message = "Name is mandatory")
    private String name;
    
    @NotBlank(message = "Email is mandatory")
    private String email;
    
    // standard constructors / setters / getters / toString
        
}

Реализация нашего класса User entity действительно довольно анемична, но она в двух словах показывает, как использовать ограничения проверки бобов для ограничения полей name и email .

Для простоты мы ограничили целевые поля, используя только ограничение @NotBlank . Кроме того, мы указали сообщения об ошибках с атрибутом message .

Поэтому, когда Spring Boot проверяет экземпляр класса, ограниченные поля не должны быть нулевыми, а их обрезанная длина должна быть больше нуля .

Кроме того, проверка Bean предоставляет множество других удобных ограничений, помимо @NotBlank. Это позволяет нам применять и комбинировать различные правила проверки для ограниченных классов. Для получения дополнительной информации, пожалуйста, ознакомьтесь с официальными документами по проверке бобов .

Поскольку мы будем использовать Spring Data JPA для сохранения пользователей в базе данных H2 в памяти, нам также необходимо определить простой интерфейс репозитория для базовой функциональности CRUD на объектах User :

@Repository
public interface UserRepository extends CrudRepository {}

4. Реализация контроллера REST

Конечно, нам нужно реализовать слой, который позволяет нам получать значения, присвоенные ограниченным полям нашего Пользователя объекта.

Поэтому мы можем проверить их и выполнить еще несколько задач, в зависимости от результатов проверки.

Spring Boot делает этот, казалось бы, сложный процесс действительно простым благодаря реализации контроллера REST.

Давайте рассмотрим реализацию контроллера REST:

@RestController
public class UserController {

    @PostMapping("/users")
    ResponseEntity addUser(@Valid @RequestBody User user) {
        // persisting the user
        return ResponseEntity.ok("User is valid");
    }
    
    // standard constructors / other methods
    
}

В контексте Spring REST реализация метода addUser() довольно стандартна.

Конечно, наиболее важной частью является использование аннотации @Valid .

Когда Spring Boot находит аргумент с аннотацией @Действительный , он автоматически загружает реализацию JSR 380 по умолчанию — Hibernate Validator — и проверяет аргумент.

Когда целевой аргумент не проходит проверку, Spring Boot создает исключение MethodArgumentNotValidException .

5. Аннотация @ExceptionHandler

Хотя очень удобно, чтобы Spring Boot автоматически проверял объект User , передаваемый методу addUser () , недостающий аспект этого процесса заключается в том, как мы обрабатываем результаты проверки.

@ExceptionHandler аннотация позволяет нам обрабатывать определенные типы исключений с помощью одного метода.

Поэтому мы можем использовать его для обработки ошибок проверки:

@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
public Map handleValidationExceptions(
  MethodArgumentNotValidException ex) {
    Map errors = new HashMap<>();
    ex.getBindingResult().getAllErrors().forEach((error) -> {
        String fieldName = ((FieldError) error).getField();
        String errorMessage = error.getDefaultMessage();
        errors.put(fieldName, errorMessage);
    });
    return errors;
}

Мы указали MethodArgumentNotValidException исключение как исключение, которое должно быть обработано . Следовательно, Spring Boot вызовет этот метод , когда указанный объект User недопустим .

Метод сохраняет имя и сообщение об ошибке после проверки каждого недопустимого поля в карте. Затем он отправляет Map обратно клиенту в виде представления JSON для дальнейшей обработки.

Проще говоря, контроллер REST позволяет нам легко обрабатывать запросы к различным конечным точкам, проверять объекты User и отправлять ответы в формате JSON.

Дизайн достаточно гибок, чтобы обрабатывать ответы контроллера через несколько веб-уровней , начиная от движков шаблонов, таких как Thymeleaf , и заканчивая полнофункциональным фреймворком JavaScript, таким как Angular .

6. Тестирование контроллера REST

Мы можем легко протестировать функциональность нашего контроллера REST с помощью интеграционного теста .

Давайте начнем издеваться над/автозапуском реализации интерфейса UserRepository вместе с экземпляром UserController и объектом MockMvc :

@RunWith(SpringRunner.class) 
@WebMvcTest
@AutoConfigureMockMvc
public class UserControllerIntegrationTest {

    @MockBean
    private UserRepository userRepository;
    
    @Autowired
    UserController userController;

    @Autowired
    private MockMvc mockMvc;

    //...
    
}

Поскольку мы тестируем только веб-слой, мы используем аннотацию @WebMvcTest . Это позволяет нам легко тестировать запросы и ответы, используя набор статических методов, реализованных классами MockMvcRequestBuilders и MockMvcResultMatchers .

Теперь давайте протестируем метод addUser() с допустимым и недопустимым объектом User , переданным в теле запроса:

@Test
public void whenPostRequestToUsersAndValidUser_thenCorrectResponse() throws Exception {
    MediaType textPlainUtf8 = new MediaType(MediaType.TEXT_PLAIN, Charset.forName("UTF-8"));
    String user = "{\"name\": \"bob\", \"email\" : \"[email protected]\"}";
    mockMvc.perform(MockMvcRequestBuilders.post("/users")
      .content(user)
      .contentType(MediaType.APPLICATION_JSON_UTF8))
      .andExpect(MockMvcResultMatchers.status().isOk())
      .andExpect(MockMvcResultMatchers.content()
        .contentType(textPlainUtf8));
}

@Test
public void whenPostRequestToUsersAndInValidUser_thenCorrectResponse() throws Exception {
    String user = "{\"name\": \"\", \"email\" : \"[email protected]\"}";
    mockMvc.perform(MockMvcRequestBuilders.post("/users")
      .content(user)
      .contentType(MediaType.APPLICATION_JSON_UTF8))
      .andExpect(MockMvcResultMatchers.status().isBadRequest())
      .andExpect(MockMvcResultMatchers.jsonPath("$.name", Is.is("Name is mandatory")))
      .andExpect(MockMvcResultMatchers.content()
        .contentType(MediaType.APPLICATION_JSON_UTF8));
    }
}

Кроме того, мы можем протестировать API контроллера СБРОСА с помощью бесплатного приложения для тестирования жизненного цикла API , такого как Postman .

7. Запуск примера приложения

Наконец, мы можем запустить наш пример проекта со стандартным методом main() :

@SpringBootApplication
public class Application {
    
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
    
    @Bean
    public CommandLineRunner run(UserRepository userRepository) throws Exception {
        return (String[] args) -> {
            User user1 = new User("Bob", "[email protected]");
            User user2 = new User("Jenny", "[email protected]");
            userRepository.save(user1);
            userRepository.save(user2);
            userRepository.findAll().forEach(System.out::println);
        };
    }
}

Как и ожидалось, мы должны увидеть пару объектов User , распечатанных в консоли.

Запрос на публикацию в http://localhost:8080/users конечная точка с допустимым Пользователем объектом вернет Строку “Пользователь допустим”.

Аналогично, запрос POST с Пользователем объектом без имени и электронной почты значений вернет следующий ответ:

{
  "name":"Name is mandatory",
  "email":"Email is mandatory"
}

8. Заключение

В этой статье мы изучили основы выполнения проверки в Spring Boot .

Как обычно, все примеры, показанные в этой статье, доступны на GitHub .