Introduction à Spring Web MVC¶
Spring Web MVC est le module Spring consacré au développement d’application Web et d’API Web. Le nom de ce module renvoie directement au modèle MVC (Modèle Vue Contrôleur). Le modèle MVC n’est pas réservé au développement Web et même, son application n’a pas vraiment de sens pour le développement d’API Web. Quoi qu’il en soit, les notions de modèle, de contrôleur et de vue sont centrales pour Spring Web MVC. Dans ce chapitre, nous rappellerons le principe du modèle MVC et nous verrons comment intégrer Spring Web MVC dans une application Spring Boot et dans une application sans Spring Boot.
Le modèle MVC¶
Le modèle MVC (Modèle Vue Contrôleur) est un modèle d’architecture pour guider la conception d’applications qui nécessitent une interaction de l’utilisateur avec le système. Il définit trois grandes catégories de responsabilité :
- Le modèle
Les classes appartenant à cette catégorie définissent les données applicatives échangées entre l’utilisateur et le système.
- La vue
Les classes appartenant à cette catégorie gèrent la représentation graphique des données et l’interface utilisateur
- Le contrôleur
Les classes appartenant à cette catégorie gèrent les interactions de l’utilisateur et la mise à jour des vues après la modification des données. Les contrôleurs assurent la cohérence entre le modèle et la vue.
Dans la logique du modèle MVC, un utilisateur interagit avec un contrôleur. Pour une application Web, une interaction avec un serveur correspond à l’envoi d’une requête HTTP. Donc les requêtes doivent être prises en charge par des contrôleurs. Ce sont eux qui alimentent le modèle avec les objets qui seront nécessaires aux vues.
le modèle est constitué par un ensemble d’objets Java qui représentent les données envoyées au serveur ou les données à afficher à l’utilisateur dans une page Web.
la vue est constituée par des objets capables de générer des pages Web, de mettre en forme les données utilisateurs… Spring Web MVC nous permet d’utiliser différentes technologies pour la prise en charge des vues. Par exemple, nous pouvons utiliser des Java Server Pages (JSP) qui est la technologie disponible depuis J2EE. Pour nos exemples, nous utiliserons Thymeleaf qui est le moteur de génération de vue recommandé par le Spring Framework (mais absolument pas imposé). Pour le développeur, la création de vue consiste principalement à écrire des modèles de vues sous la forme de fichiers HTML.
le contrôleur est un composant chargé de valider les paramètres de la requête avant de les transmettre à la couche de service pour traitement. Une fois ce traitement terminé, c’est le contrôleur qui met à jour le modèle et le transmet à une vue. Nous verrons au prochain chapitre que Spring Web MVC fournit un jeu d’annotations particulier pour nous permettre de développer des contrôleurs.
Intégration de Spring Web MVC¶
Spring Web MVC est un module pour le développement d’application Web ou d’API Web. Donc, cela suppose le recours à un serveur pour traiter les requêtes. Le Spring Framework ne fournit pas de serveur Web. Pour exécuter une application Spring Web MVC nous avons donc besoin de déployer notre application dans un serveur.
Comme il a été dit dans l’introduction générale au Spring Framework, ce dernier a été conçu comme une approche différente en terme d’architecture par rapport à J2EE (puis Java EE et Jakarta EE). La différence fondamentale est que chaque application Spring embarque son propre conteneur avec les services dont elle a besoin. Donc une application Spring Web MVC n’a pas besoin d’un serveur d’application Java EE complet comme Wildfly, GlassFish ou TomEE. Elle peut s’exécuter dans un conteneur Web plus léger comme Tomcat ou Jetty qui offre le service minimal dont une application Spring Web MVC à besoin : le lancement d’un serveur HTTP et la possibilité de déléguer le traitement des requêtes au code de l’application.
Le choix d’utiliser ou non Spring Boot pour configurer votre application va être déterminant pour l’intégration de Spring Web MVC et le déploiement de votre application.
Intégration dans une application avec Spring Boot¶
Spring Boot est un projet conçu pour simplifier considérablement la configuration des applications basées sur le Spring Framework. On peut donc s’attendre à ce que l’intégration de Spring Web MVC se fasse facilement… ce qui est effectivement le cas.
Pour intégrer Spring Web MVC, vous devez rajouter une dépendance au module
spring-boot-starter-web
.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Il est fortement recommandé d’ajouter également une dépendance au module
spring-boot-devtools
. Ce dernier est très pratique pour la phase de développement.
Il permet notamment le redémarrage à chaud du serveur lorsqu’on effectue une
modification dans le code source.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
Lorsque la dépendance à spring-boot-starter-web
est présente, le comportement
de Spring Boot change au lancement de votre application. Il va automatiquement
lancer un serveur HTTP (Tomcat par défaut) et déployer votre application dans
ce serveur. Du point de vue de l’application, cela change radicalement le choix
d’architecture. Plutôt que de disposer d’un serveur central Java EE dans lequel
nous déployons nos applications, c’est chaque application qui dispose de son
serveur embarqué.
Le serveur est configurable à travers les nombreux paramètres fournis par
Spring Boot.
Le plus utile pour démarrer est sans doute le paramètre server.port
qui
permet de donner le port d’écoute du serveur (8080
par défaut). Donc, si
vous voulez que votre serveur écoute sur le port 9090
, il suffit d’ajouter
dans le fichier application.properties
:
server.port = 9090
Astuce
Même si vous développez une application Spring Boot avec un serveur embarqué, il est très facile de la transformer en application Web Java EE prête à être déployée dans un serveur central.
Pour cela, vous devez générer une application War. Si vous utilisez Maven
pour gérer votre projet, il vous suffit de changer ou d’ajouter la balise
<packaging>
dans votre fichier pom.xml
pour indiquer un packaging
de type war
:
<groupId>dev.gayerie</groupId>
<artifactId>monapplication</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
Vous devez également fournir dans votre code une classe qui hérite de la classe SpringBootServletInitializer et qui va lancer votre application :
1 2 3 4 5 6 7 8 9 10 11 12 13 | package dev.gayerie;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
public class MyWebApplication extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(MyApplication.class);
}
}
|
Dans l’exemple ci-dessus, la classe MyApplication
référencée à la ligne
10 est tout simplement la classe principale de votre application Spring
Boot, celle qui déclare la méthode main
.
Pour que le déploiement fonctionne, il faut que le serveur supporte l’API Servlet version 3.0 ou plus (ce qui est le cas de tous les serveurs d’application Java EE récents).
Intégration dans une application sans Spring Boot¶
Une application Spring Web MVC suppose d’être déployée dans un conteneur Web
Java EE. Tomcat ou Jetty peuvent suffire pour cela. Vous devez bien évidemment
générer une application War. Si vous utilisez Maven pour gérer votre projet,
il vous suffit de changer ou d’ajouter la balise <packaging>
dans votre
fichier pom.xml
pour indiquer un packaging de type war
:
<groupId>dev.gayerie</groupId>
<artifactId>monapplication</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
Vous devez également déclarer une dépendance au module spring-mvc
ainsi
qu’à l’API Servlet :
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.1</version>
</dependency>
Vous allez avoir besoin d’une classe pour initialiser la Servlet de votre application et le contexte d’application Spring. Depuis la version 3.0 de l’API Servlet, il est possible de réaliser cette phase d’initialisation dans une classe Java implémentant l’interface WebApplicationInitializer.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | package dev.gayerie;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
public class MainWebAppInitializer implements WebApplicationInitializer {
@Override
public void onStartup(final ServletContext sc) throws ServletException {
// Chargement du contexte d'application
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(WebAppConfiguration.class);
// Création de la servlet
DispatcherServlet servlet = new DispatcherServlet(context);
ServletRegistration.Dynamic registration = sc.addServlet("app", servlet);
registration.setLoadOnStartup(1);
registration.addMapping("/");
}
}
|
Les lignes 18 à 21 créent et initialisent une servlet qui est de type
DispatcherServlet. Cette classe est fournie par Spring Web MVC. Notez qu’à
la ligne 21, on configure la servlet pour répondre aux requêtes concernant les
chemins à partir de /
, c’est à dire toutes les requêtes concernant notre
application.
Les lignes 15 et 16 créent un contexte d’application spécifique aux applications
Web et enregistrent une classe WebAppConfiguration
. Cette classe doit
aussi être fournie par notre application et correspond au point d’entrée
pour la configuration du contexte d’application.
1 2 3 4 5 6 7 8 9 10 11 12 | package dev.gayerie;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@EnableWebMvc
@Configuration
@ComponentScan
public class WebAppConfiguration implements WebMvcConfigurer {
}
|
Cette classe est identifiée comme une classe de configuration Spring grâce à l’annotation @Configuration. L’annotation @EnableWebMvc sert (comme son nom l’indique explicitement) à activer le support de Spring Web MVC, comme par exemple, activer la prise en charge des classes de contrôleurs.
L’encodage des paramètres de requêtes¶
L’encodage des paramètres des requêtes est source d’erreur dans le développement
d’applications Web. En effet, même si l’encodage le plus couramment utilisé
actuellement est UTF-8, il ne faut pas oublier que l’encodage par défaut sur le
Web est Latin-1 (ISO-8859-1). Selon les navigateurs (et notamment suivant les
versions des navigateurs), il peut y avoir des comportements légèrement différents.
Ce n’est pas parce qu’une page HTML est encodée en UTF-8 que le formulaire qu’elle
contient soumettra nécessairement des données en UTF-8. Pour contrôler au mieux
le comportement des navigateurs, il est conseillé d’utiliser systématiquement
l’attribut accept-charset
sur la balise <form>
. Cet attribut permet
justement de spécifier l’encodage à utiliser pour envoyer les données au serveur :
<form action="..." method="post" accept-charset="utf-8">
<input type="text" name="nom">
<input type="submit">Envoyer</input>
</form>
Côté serveur, vous devez vous assurer que l’encodage utilisé pour traiter les paramètres des requêtes est correctement positionné.
Pour une application avec Spring Boot, l’encodage par défaut pour les paramètres
des requêtes est UTF-8. Si vous voulez le modifier, il faut déclarer la propriété
server.tomcat.uri-encoding
dans le fichier application.properties
:
server.tomcat.uri-encoding = iso-8859-1
Pour une application sans Spring Boot, l’encodage par défaut sera celui du serveur sur lequel vous allez déployer votre application. Pour un serveur Tomcat par exemple, l’encodage du serveur est Latin-1 (ISO-8859-1). Si vous voulez utiliser l’encodage UTF-8, plutôt que de modifier la configuration de votre serveur, vous pouvez ajouter un filtre de requêtes dans votre application pour positionner l’encodage à UTF-8 à l’arrivée de chaque nouvelle requête.
Pour cela, ajoutez la ligne de code suivante dans la classe d’initialisation de votre application :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | package dev.gayerie;
import java.util.EnumSet;
import javax.servlet.DispatcherType;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.DispatcherServlet;
public class MainWebAppInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext sc) throws ServletException {
// Chargement du contexte d'application
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(WebAppConfiguration.class);
// Création de la servlet
DispatcherServlet servlet = new DispatcherServlet(context);
ServletRegistration.Dynamic registration = sc.addServlet("app", servlet);
registration.setLoadOnStartup(1);
registration.addMapping("/");
// Ajout du filtre UTF-8 pour les paramètres des requêtes
sc.addFilter("characterEncoding", new CharacterEncodingFilter("UTF-8"))
.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*");
}
}
|