Il va simplifier l'écriture des HttpServlet:
Source: Documentation de Spring MVC
Map<String,Object>) avec les informations qu'il veut voir afficher. Il ne renvoie pas du code HTML mais le nom d'un template HTML
@Controller
public class HelloController {
@GetMapping("/hello")
public String greeting(Model model) {
model.addAttribute("name", "Arnaud");
return "hello";
}
}
greeting est /hello. Il ne traitera que les requêtes GET.greeting renvoie le nom de la vue qui doit être utilisée.
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Hello Spring-MVC</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<p th:text="'Hello, ' + ${name} + '!'" />
</body>
</html>
["name" -> "Arnaud].
@GetMapping("/shopping")
public String greeting(Model model) {
model.addAttribute("groceries", List.of("Potatoes","Milk","Sugar"));
return "shopping";
}
<body>
<h2>My grocery list</h2>
<ul>
<li th:each="item : ${groceries}" th:text="${item}"></li>
</ul>
</body>
Le HTML produit contient une liste de tous les éléments de l'attribut groceries du Model.
ThymeLeaf peut injecter la valeur par défaut des champs d'un formulaire à partir d'un objet du Model.
@GetMapping("/rectangledefault")
public String rectangleDefault(Model model){
var rectangle = new Rectangle(10,15);
model.addAttribute("rectangle",rectangle);
return "rectangledefault";
}
<form th:action="@{/rectangle}" th:object="${rectangle}" method="post">
<h2>Rectangle area calculator</h2>
<div><label for="h">Height:</label>
<input id="h" type="text" name="height" th:field="*{height}"></div>
<div><label for="w">Width:< /label>
<input type="text" id="w" name="width" th:field="*{width}"></div>
<div><button type="submit">Compute</button></div>
</form>
Lisez la documentation pour voir tout ce que ThymeLeaf sait faire.
@GetMapping("/goodbye/{name}/{uid}")
public String goodbye(@PathVariable("name") String username,
@PathVariable("uid") int uid,
Model model) {
model.addAttribute("name", username);
model.addAttribute("uid",uid);
return "goodbye";
}
Les paramètres name et uid sont extraits de la route:
Exemple: http://localhost:8080/goodbye/Arnaud/42
@GetMapping("/square")
public String goodbye(@RequestParam(name="number", required=true) long n,
Model model) {
model.addAttribute("number", n);
model.addAttribute("numberSquared", n*n);
return "square";
}
Le paramètre number est extrait de la requête HTTP GET:
Exemple: http://localhost:8080/square?number=42
Une des fonctionnalités de Spring MVC est de faciliter la récupération des données venant d'un formulaire.
Spring MVC va créer automagiquement un objet Java à partir des données du formulaire.
Le lien entre les données du formulaire et les champs de l'objet Java se font par les noms.
@Controller
public class RectangleController {
@GetMapping("/rectangle")
public String rectangleForm(){
return "rectangle";
}
}
View : rectangle.html
<form th:action="@{/rectangle}" method="post">
<h2>Rectangle area calculator</h2>
<div><label for="h">Height:</label>
<input id="h" type="text" name="height"></div>
<div><label for="w">Width:</label>
<input type="text" id="w" name="width"></div>
<div><button type="submit">Compute</button></div>
</form>
public record Rectangle(int width, int height) {
public int area() { return width*height; }
}
@PostMapping("/rectangle")
public String processForm(@ModelAttribute("rectangle-model") Rectangle rectangle){
return "rectangle-result";
}
L'annotation @ModelAttribute("rectangle-model") a pour effet de:
Rectangle rectangle à partir des informations du formulaire,Model avec la clé rectangle-model.Le lien entre les éléments du formulaire et les champs du record se font par les noms. Si l'on veut utiliser des noms différents entre le formulaire et le record, on peut utiliser l'annotation @BindParam sur les champs du record pour donner le nom correspondant dans le formulaire.
public class Rectangle {
private final int width;
private final int height;
public Rectangle(int width, int height){
this.width = width;
this.height = height;
}
public int area(){return width*height;}
}
@PostMapping("/rectangle")
public String processForm(@ModelAttribute("rectangle-model") Rectangle rectangle){
return "rectangle-result";
}
Si l'on utilise une classe, il faut privilégier l'utilisation du constructeur.
public class Rectangle {
private int width;
private int height;
public Rectangle(){}
// setters and getters
public int area(){return width*height;}
}
@PostMapping("/rectangle")
public String processForm(@ModelAttribute("rectangle-model") Rectangle rectangle){
return "rectangle-result";
}
Dans l'approche historique (qui est maintenant déconseillée), Spring crée un objet en appelant le constructeur sans paramètres puis le remplit en utilisant les setters. Il faut comprendre le code mais il est déconseiller de l'utiliser.
Règle d'or de la sécurité: Never trust the user!
On valide toujours tous les champs de tous les formulaires sur le back-end.
En pratique, on rajoute de la validation aussi sur le front-end pour rendre le site plus agréable à utiliser. Typiquement du JavaScript qui va valider les champs du formulaire et griser le bouton d'envoi si ils sont invalides.
La validation sur le front-end seule ne suffit pas ! On peut toujours accéder au back-end directement sans passer par le front-end.
@PostMapping("/rectanglebetter")
public String processForm(@ModelAttribute("rectangle") Rectangle rectangle,
BindingResult result,
Model model){
if (result.hasErrors()){
return "error";
}
if (rectangle.width()<0 || rectangle.height()<0){
return "error";
}
return "parameter-result";
}
Les erreurs de convertion sont stocker dans BindingResult result. Par exemple, si l'utilisateur entre Toto pour la largeur.
Le code peut devenir fastidieux.
Spring MVC permet de valider les champs de l'objet qu'il remplit grâce à l'API Bean Validation.
public record Rectangle( @Min(value=0,message="Width must be positive") int width,
@Min(value=0,message="Height must be positive") int height) {
public int area() { return width*height; }
}
@PostMapping("/rectangle")
public String processForm(@ModelAttribute("rectangle-model") @Valid Rectangle rectangle){
return "rectangle-result";
}
Thymeleaf permet d'incorporer directement les messages d'erreurs de validation dans le formulaire.
Pour simplifier, la gestion des dépendances, la configuration de Spring et l'injection de tous ces composants, nous allons utiliser Spring Boot.
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@SpringBootApplication est équivalente à @Configuration, @EnableAutoConfiguration et @ComponentScan.En résumé, Spring Boot est un framework Java basé sur Spring qui simplifie la création d’applications en fornissant des configurations automatiques.
Il permet d'éviter de réécrire à chaque fois le code de configuration de Spring. En contre-partie, Spring Boot fait des choix de configuration par défaut assez forts qu'il faut savoir remettre en question (cf. Open Session in View pattern).