Validation dans Spring Boot

1. Vue d'ensemble

Lorsqu'il s'agit de valider les entrées de l'utilisateur, Spring Boot fournit un support solide pour cette tâche commune, mais critique, dès la sortie de la boîte.

Bien que Spring Boot prenne en charge l'intégration transparente avec les validateurs personnalisés, la norme de facto pour effectuer la validation est Hibernate Validator , l'implémentation de référence du framework Bean Validation.

Dans ce didacticiel, nous verrons comment valider les objets de domaine dans Spring Boot .

2. Les dépendances de Maven

Dans ce cas, nous allons apprendre à valider les objets de domaine dans Spring Boot en créant un contrôleur REST de base.

Le contrôleur prendra d'abord un objet de domaine, puis il le validera avec Hibernate Validator, et enfin il le persistera dans une base de données H2 en mémoire.

Les dépendances du projet sont assez standards:

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

Comme indiqué ci-dessus, nous avons inclus spring-boot-starter-web dans notre fichier pom.xml car nous en aurons besoin pour créer le contrôleur REST. De plus, assurons-nous de vérifier les dernières versions de spring-boot-starter-jpa et la base de données H2 sur Maven Central.

À partir de Boot 2.3, nous devons également ajouter explicitement la dépendance spring-boot-starter-validation :

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

3. Une classe de domaine simple

Les dépendances de notre projet étant déjà en place, nous devons ensuite définir un exemple de classe d'entité JPA, dont le rôle sera uniquement de modéliser les utilisateurs.

Jetons un coup d'œil à cette classe:

@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 }

L'implémentation de notre classe d'entité User est en effet assez anémique, mais elle montre en un mot comment utiliser les contraintes de Bean Validation pour contraindre les champs de nom et d' e - mail .

Par souci de simplicité, nous avons contraint les champs cibles en utilisant uniquement la contrainte @NotBlank . De plus, nous avons spécifié les messages d'erreur avec l' attribut message .

Par conséquent, lorsque Spring Boot valide l'instance de classe, les champs contraints ne doivent pas être nuls et leur longueur tronquée doit être supérieure à zéro .

De plus, Bean Validation fournit de nombreuses autres contraintes pratiques en plus de @NotBlank. Cela nous permet d'appliquer et de combiner différentes règles de validation aux classes contraintes. Pour plus d'informations, veuillez lire la documentation officielle de validation des bean.

Étant donné que nous utiliserons Spring Data JPA pour enregistrer les utilisateurs dans la base de données H2 en mémoire, nous devons également définir une interface de référentiel simple pour avoir les fonctionnalités CRUD de base sur les objets utilisateur :

@Repository public interface UserRepository extends CrudRepository {}

4. Implémentation d'un contrôleur REST

Bien sûr, nous devons implémenter une couche qui nous permet d'obtenir les valeurs affectées aux champs contraints de notre objet User .

Par conséquent, nous pouvons les valider et effectuer quelques tâches supplémentaires, en fonction des résultats de la validation.

Spring Boot rend ce processus apparemment complexe très simple grâce à l'implémentation d'un contrôleur REST.

Regardons l'implémentation du contrôleur 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 } 

Dans un contexte Spring REST, l'implémentation de la méthode addUser () est assez standard.

Bien sûr, la partie la plus pertinente est l'utilisation de l' annotation @Valid .

Lorsque Spring Boot trouve un argument annoté avec @Valid , il amorce automatiquement l'implémentation JSR 380 par défaut - Hibernate Validator - et valide l'argument.

Lorsque l'argument cible échoue à passer la validation, Spring Boot lève une exception MethodArgumentNotValidException .

5. L' annotation @ExceptionHandler

Bien qu'il soit vraiment pratique que Spring Boot valide automatiquement l' objet User à la méthode addUser () , la facette manquante de ce processus est la façon dont nous traitons les résultats de la validation.

L' annotation @ExceptionHandler nous permet de gérer des types d'exceptions spécifiés via une seule méthode.

Par conséquent, nous pouvons l'utiliser pour traiter les erreurs de validation:

@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; }

Nous avons spécifié l' exception MethodArgumentNotValidException comme exception à gérer. Par conséquent, Spring Boot appellera cette méthode lorsque l' objet User spécifié n'est pas valide .

La méthode stocke le nom et le message d'erreur de post-validation de chaque champ non valide dans une carte. Ensuite, il renvoie la carte au client en tant que représentation JSON pour un traitement ultérieur.

En termes simples, le contrôleur REST nous permet de traiter facilement les demandes vers différents points de terminaison, de valider les objets utilisateur et d'envoyer les réponses au format JSON.

La conception est suffisamment flexible pour gérer les réponses des contrôleurs à travers plusieurs niveaux Web, allant des moteurs de modèles tels que Thymeleaf, à un cadre JavaScript complet tel que Angular.

6. Test du contrôleur REST

Nous pouvons facilement tester la fonctionnalité de notre contrôleur REST avec un test d'intégration.

Let's start mocking/autowiring the UserRepository interface implementation, along with the UserController instance, and a MockMvc object:

@RunWith(SpringRunner.class) @WebMvcTest @AutoConfigureMockMvc public class UserControllerIntegrationTest { @MockBean private UserRepository userRepository; @Autowired UserController userController; @Autowired private MockMvc mockMvc; //... } 

Since we're only testing the web layer, we use the @WebMvcTest annotation. It allows us to easily test requests and responses using the set of static methods implemented by the MockMvcRequestBuilders and MockMvcResultMatchers classes.

Now let's test the addUser() method with a valid and an invalid User object passed in the request body:

@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)); } } 

In addition, we can test the REST controller API using a free API life cycle testing application, such as Postman.

7. Exécution de l'exemple d'application

Enfin, nous pouvons exécuter notre exemple de projet avec une méthode standard 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); }; } } 

Comme prévu, nous devrions voir quelques objets User imprimés dans la console.

Une requête POST au point de terminaison // localhost: 8080 / users avec un objet User valide renverra la chaîne «L'utilisateur est valide».

De même, une requête POST avec un objet User sans nom et sans valeur de courrier électronique renverra la réponse suivante:

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

8. Conclusion

Dans cet article, nous avons appris les bases de l'exécution de la validation dans Spring Boot .

Comme d'habitude, tous les exemples présentés dans cet article sont disponibles à l'adresse over sur GitHub.