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]
.
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
.@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"></div> <div><label for="w">Width:</label> <input type="text" id="w" name="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.