Validation de formulaire avec AngularJS et Spring MVC

1. Vue d'ensemble

La validation n'est jamais aussi simple que prévu. Et bien sûr, valider les valeurs saisies par un utilisateur dans une application est très important pour préserver l'intégrité de nos données.

Dans le contexte d'une application Web, la saisie des données est généralement effectuée à l'aide de formulaires HTML et nécessite une validation côté client et côté serveur.

Dans ce didacticiel, nous examinerons la mise en œuvre de la validation côté client de l'entrée de formulaire à l'aide d'AngularJS et de la validation côté serveur à l'aide du framework Spring MVC .

Cet article se concentre sur Spring MVC. Notre article Validation dans Spring Boot décrit comment effectuer des validations dans Spring Boot.

2. Dépendances de Maven

Pour commencer, ajoutons les dépendances suivantes:

 org.springframework spring-webmvc 4.3.7.RELEASE   org.hibernate hibernate-validator 5.4.0.Final   com.fasterxml.jackson.core jackson-databind 2.8.7 

Les dernières versions de spring-webmvc, hibernate-validator et jackson-databind peuvent être téléchargées depuis Maven Central.

3. Validation à l'aide de Spring MVC

Une application ne doit jamais s'appuyer uniquement sur la validation côté client, car cela peut être facilement contourné. Pour éviter que des valeurs incorrectes ou malveillantes ne soient enregistrées ou ne provoquent une exécution incorrecte de la logique d'application, il est important de valider également les valeurs d'entrée côté serveur.

Spring MVC offre la prise en charge de la validation côté serveur à l'aide des annotations de spécification JSR 349 ​​Bean Validation . Pour cet exemple, nous utiliserons l'implémentation de référence de la spécification, qui est hibernate-validator .

3.1. Le modèle de données

Créons une classe User dont les propriétés sont annotées avec les annotations de validation appropriées:

public class User { @NotNull @Email private String email; @NotNull @Size(min = 4, max = 15) private String password; @NotBlank private String name; @Min(18) @Digits(integer = 2, fraction = 0) private int age; // standard constructor, getters, setters }

Les annotations utilisées ci-dessus appartiennent à la spécification JSR 349 , à l'exception de @Email et @NotBlank , qui sont spécifiques à la bibliothèque hibernate-validator .

3.2. Contrôleur MVC Spring

Créons une classe de contrôleur qui définit un point de terminaison / user , qui sera utilisé pour enregistrer un nouvel objet User dans une liste .

Afin de permettre la validation de l' objet User reçu via les paramètres de demande, la déclaration doit être précédée de l' annotation @Valid , et les erreurs de validation seront conservées dans une instance BindingResult .

Pour déterminer si l'objet contient des valeurs non valides, nous pouvons utiliser la méthode hasErrors () de BindingResult .

Si hasErrors () renvoie true , nous pouvons renvoyer un tableau JSON contenant les messages d'erreur associés aux validations qui n'ont pas réussi. Sinon, nous ajouterons l'objet à la liste:

@PostMapping(value = "/user") @ResponseBody public ResponseEntity saveUser(@Valid User user, BindingResult result, Model model) { if (result.hasErrors()) { List errors = result.getAllErrors().stream() .map(DefaultMessageSourceResolvable::getDefaultMessage) .collect(Collectors.toList()); return new ResponseEntity(errors, HttpStatus.OK); } else { if (users.stream().anyMatch(it -> user.getEmail().equals(it.getEmail()))) { return new ResponseEntity( Collections.singletonList("Email already exists!"), HttpStatus.CONFLICT); } else { users.add(user); return new ResponseEntity(HttpStatus.CREATED); } } }

Comme vous pouvez le voir, la validation côté serveur ajoute l'avantage d'avoir la possibilité d'effectuer des vérifications supplémentaires qui ne sont pas possibles côté client.

Dans notre cas, nous pouvons vérifier si un utilisateur avec le même email existe déjà - et renvoyer un statut de 409 CONFLICT si c'est le cas.

Nous devons également définir notre liste d'utilisateurs et l'initialiser avec quelques valeurs:

private List users = Arrays.asList( new User("[email protected]", "pass", "Ana", 20), new User("[email protected]", "pass", "Bob", 30), new User("[email protected]", "pass", "John", 40), new User("[email protected]", "pass", "Mary", 30));

Ajoutons également un mappage pour récupérer la liste des utilisateurs en tant qu'objet JSON:

@GetMapping(value = "/users") @ResponseBody public List getUsers() { return users; }

Le dernier élément dont nous avons besoin dans notre contrôleur Spring MVC est un mappage pour retourner la page principale de notre application:

@GetMapping("/userPage") public String getUserProfilePage() { return "user"; }

Nous allons jeter un œil à la page user.html plus en détail dans la section AngularJS.

3.3. Configuration de Spring MVC

Ajoutons une configuration MVC de base à notre application:

@Configuration @EnableWebMvc @ComponentScan(basePackages = "com.baeldung.springmvcforms") class ApplicationConfiguration implements WebMvcConfigurer { @Override public void configureDefaultServletHandling( DefaultServletHandlerConfigurer configurer) { configurer.enable(); } @Bean public InternalResourceViewResolver htmlViewResolver() { InternalResourceViewResolver bean = new InternalResourceViewResolver(); bean.setPrefix("/WEB-INF/html/"); bean.setSuffix(".html"); return bean; } }

3.4. Initialisation de l'application

Créons une classe qui implémente l' interface WebApplicationInitializer pour exécuter notre application:

public class WebInitializer implements WebApplicationInitializer { public void onStartup(ServletContext container) throws ServletException { AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); ctx.register(ApplicationConfiguration.class); ctx.setServletContext(container); container.addListener(new ContextLoaderListener(ctx)); ServletRegistration.Dynamic servlet = container.addServlet("dispatcher", new DispatcherServlet(ctx)); servlet.setLoadOnStartup(1); servlet.addMapping("/"); } }

3.5. Test de la validation Spring Mvc à l'aide de Curl

Avant d'implémenter la section client AngularJS, nous pouvons tester notre API en utilisant cURL avec la commande:

curl -i -X POST -H "Accept:application/json" "localhost:8080/spring-mvc-forms/user?email=aaa&password=12&age=12"

La réponse est un tableau contenant les messages d'erreur par défaut:

[ "not a well-formed email address", "size must be between 4 and 15", "may not be empty", "must be greater than or equal to 18" ]

4. Validation AngularJS

La validation côté client est utile pour créer une meilleure expérience utilisateur, car elle fournit à l'utilisateur des informations sur la manière de soumettre avec succès des données valides et lui permet de continuer à interagir avec l'application.

La bibliothèque AngularJS prend en charge l'ajout d'exigences de validation sur les champs de formulaire, la gestion des messages d'erreur et le style des formulaires valides et non valides.

Tout d'abord, créons un module AngularJS qui injecte le module ngMessages , qui est utilisé pour les messages de validation:

var app = angular.module('app', ['ngMessages']);

Ensuite, créons un service et un contrôleur AngularJS qui consommeront l'API intégrée dans la section précédente.

4.1. Le service AngularJS

Notre service aura deux méthodes qui appellent les méthodes du contrôleur MVC - une pour enregistrer un utilisateur et une pour récupérer la liste des utilisateurs:

app.service('UserService',['$http', function ($http) { this.saveUser = function saveUser(user){ return $http({ method: 'POST', url: 'user', params: {email:user.email, password:user.password, name:user.name, age:user.age}, headers: 'Accept:application/json' }); } this.getUsers = function getUsers(){ return $http({ method: 'GET', url: 'users', headers:'Accept:application/json' }).then( function(response){ return response.data; } ); } }]);

4.2. Le contrôleur AngularJS

Le contrôleur UserCtrl injecte le UserService , appelle les méthodes de service et gère la réponse et les messages d'erreur:

app.controller('UserCtrl', ['$scope','UserService', function ($scope,UserService) { $scope.submitted = false; $scope.getUsers = function() { UserService.getUsers().then(function(data) { $scope.users = data; }); } $scope.saveUser = function() { $scope.submitted = true; if ($scope.userForm.$valid) { UserService.saveUser($scope.user) .then (function success(response) { $scope.message = 'User added!'; $scope.errorMessage = ''; $scope.getUsers(); $scope.user = null; $scope.submitted = false; }, function error(response) { if (response.status == 409) { $scope.errorMessage = response.data.message; } else { $scope.errorMessage = 'Error adding user!'; } $scope.message = ''; }); } } $scope.getUsers(); }]);

Nous pouvons voir dans l'exemple ci-dessus que la méthode de service n'est appelée que si la propriété $ valid de userForm est vraie. Pourtant, dans ce cas, il y a la vérification supplémentaire des e-mails en double, qui ne peut être effectuée que sur le serveur et est gérée séparément dans la fonction error () .

Notez également qu'une variable soumise est définie qui nous dira si le formulaire a été soumis ou non.

Initialement, cette variable sera fausse , et lors de l'appel de la méthode saveUser () , elle deviendra vraie . Si nous ne voulons pas que les messages de validation s'affichent avant que l'utilisateur soumette le formulaire, nous pouvons utiliser la variable soumise pour éviter cela.

4.3. Form Using AngularJS Validation

In order to make use of the AngularJS library and our AngularJS module, we will need to add the scripts to our user.html page:

Then we can use our module and controller by setting the ng-app and ng-controller properties:

Let's create our HTML form:

 ... 

Note that we have to set the novalidate attribute on the form in order to prevent default HTML5 validation and replace it with our own.

The ng-class attribute adds the form-error CSS class dynamically to the form if the submitted variable has a value of true.

The ng-submit attribute defines the AngularJS controller function that will be called when the form in submitted. Using ng-submit instead of ng-click has the advantage that it also responds to submitting the form using the ENTER key.

Now let's add the four input fields for the User attributes:

Email:  Password:  Name:  Age: 

Each input field has a binding to a property of the user variable through the ng-model attribute.

For setting validation rules, we use the HTML5 required attribute and several AngularJS-specific attributes: ng-minglength, ng-maxlength, ng-min, and ng-trim.

For the email field, we also use the type attribute with a value of email for client-side email validation.

In order to add error messages corresponding to each field, AngularJS offers the ng-messages directive, which loops through an input's $errors object and displays messages based on each validation rule.

Let's add the directive for the email field right after the input definition:

Invalid email!

Email is required!

Similar error messages can be added for the other input fields.

We can control when the directive is displayed for the email field using the ng-show property with a boolean expression. In our example, we display the directive when the field has an invalid value, meaning the $invalid property is true, and the submitted variable is also true.

Only one error message will be displayed at a time for a field.

We can also add a check mark sign (represented by HEX code character ✓) after the input field in case the field is valid, depending on the $valid property:

AngularJS validation also offers support for styling using CSS classes such as ng-valid and ng-invalid or more specific ones like ng-invalid-required and ng-invalid-minlength.

Let's add the CSS property border-color:red for invalid inputs inside the form's form-error class:

.form-error input.ng-invalid { border-color:red; }

We can also show the error messages in red using a CSS class:

.error-messages { color:red; }

After putting everything together, let's see an example of how our client-side form validation will look when filled out with a mix of valid and invalid values:

5. Conclusion

In this tutorial, we've shown how we can combine client-side and server-side validation using AngularJS and Spring MVC.

Comme toujours, le code source complet des exemples est disponible à l'adresse over sur GitHub.

Pour afficher l'application, accédez à l' URL / userPage après l'avoir exécutée.