
Redirections et forwards
Comprenez la différence entre les forwards (côté serveur) et les redirections (côté client) dans Spring MVC et apprenez à les utiliser efficacement.
Naviguer après le traitement : Forwards et Redirections
Dans une application web, une fois qu'un contrôleur a traité une requête (par exemple, après la soumission d'un formulaire ou l'exécution d'une action), il est souvent nécessaire de diriger l'utilisateur vers une autre page ou ressource. Spring MVC offre deux mécanismes principaux pour réaliser cela : le forward (transfert côté serveur) et la redirection (côté client).
Comprendre la différence fondamentale entre ces deux approches est crucial pour construire des flux de navigation corrects, éviter des problèmes comme la soumission multiple de formulaires et gérer efficacement l'état entre les requêtes.
Ces mécanismes sont généralement déclenchés en retournant une chaîne spéciale préfixée (forward: ou redirect:) depuis une méthode de handler dans un @Controller.
Le Forward : Transfert interne côté serveur
Un forward (parfois appelé dispatching) est un mécanisme entièrement côté serveur. Lorsque vous effectuez un forward depuis un handler vers une autre ressource (qui peut être une vue ou même un autre handler), le serveur transfère le contrôle à cette ressource sans informer le client (navigateur).
Caractéristiques clés du forward :
- URL inchangée : L'URL affichée dans la barre d'adresse du navigateur ne change pas. L'utilisateur n'est pas conscient du transfert interne.
- Même requête : Le forward se produit au sein de la même requête HTTP. L'objet
HttpServletRequestoriginal, y compris ses attributs, paramètres et en-têtes, est généralement transmis à la ressource cible. Les données ajoutées auModelsont donc préservées. - Performance : C'est généralement plus rapide qu'une redirection car il n'y a pas de nouvel aller-retour réseau avec le client.
Pour effectuer un forward dans Spring MVC, vous retournez une chaîne préfixée par forward: suivie du chemin vers la ressource cible (généralement une autre URL gérée par un contrôleur ou directement le chemin vers une vue).
@Controller
@RequestMapping("/legacy")
public class LegacyController {
@GetMapping("/old-path")
public String handleOldPath(Model model) {
System.out.println("Traitement dans /legacy/old-path");
model.addAttribute("message", "Donnée de l'ancien chemin");
// Transfère la requête en interne vers /new/path (sans changer l'URL du navigateur)
return "forward:/new/path";
}
}
@Controller
@RequestMapping("/new")
public class NewController {
@GetMapping("/path")
public String handleNewPath(Model model) {
System.out.println("Traitement dans /new/path après forward");
// Le message ajouté dans handleOldPath est accessible ici
System.out.println("Message reçu: " + model.getAttribute("message"));
model.addAttribute("finalMessage", "Contenu de la nouvelle page");
return "newView"; // Résout vers la vue /templates/newView.html
}
}
Cas d'usage typiques : transfert interne de logique, application de décorateurs (layouts) autour d'une vue principale. Cependant, il est moins utilisé pour les flux post-action que la redirection.
La Redirection : Instruction côté client
Une redirection implique le client (navigateur). Lorsque vous effectuez une redirection, le serveur renvoie une réponse HTTP spéciale au client (typiquement avec un statut 302 Found ou 303 See Other) contenant l'en-tête Location avec la nouvelle URL à laquelle le navigateur doit se rendre.
Caractéristiques clés de la redirection :
- URL modifiée : Le navigateur reçoit la réponse de redirection et effectue immédiatement une nouvelle requête HTTP GET vers l'URL indiquée dans l'en-tête
Location. L'URL dans la barre d'adresse du navigateur est mise à jour avec cette nouvelle URL. - Nouvelle requête : Puisqu'il s'agit d'une nouvelle requête, l'objet
HttpServletRequestoriginal, ses attributs (y compris ceux duModel) et ses paramètres sont perdus. C'est une requête complètement distincte. - Méthode GET : La nouvelle requête effectuée par le navigateur suite à une redirection est presque toujours une requête GET, quelle que soit la méthode de la requête originale (par exemple, POST).
Pour effectuer une redirection dans Spring MVC, vous retournez une chaîne préfixée par redirect: suivie de l'URL de destination.
@Controller
public class FormController {
@PostMapping("/submit-data")
public String handleSubmit(DataForm dataForm /* ... d'autres params */) {
// 1. Traiter les données soumises (ex: sauvegarder en base)
Long newId = dataService.save(dataForm);
System.out.println("Données sauvegardées avec ID: " + newId);
// 2. Rediriger vers une page de succès (ou la page de l'élément créé)
// Le navigateur fera une NOUVELLE requête GET vers /success
// return "redirect:/success";
// Ou rediriger vers la page de détails de l'élément créé
return "redirect:/items/" + newId;
}
@GetMapping("/success")
public String showSuccessPage() {
System.out.println("Affichage de la page de succès après redirection.");
return "successView"; // Vue /templates/successView.html
}
@GetMapping("/items/{id}")
public String showItemPage(@PathVariable Long id, Model model) {
Item item = dataService.findById(id);
model.addAttribute("item", item);
System.out.println("Affichage de l'item " + id + " après redirection.");
return "itemView"; // Vue /templates/itemView.html
}
}
Cas d'usage principal : Le pattern Post-Redirect-Get (PRG). Après une soumission de formulaire réussie (POST), on redirige vers une page de résultat (GET). Cela empêche l'utilisateur de resoumettre accidentellement le formulaire s'il rafraîchit la page de résultat (car rafraîchir rejouera la requête GET, pas la requête POST originale).
Passer des données lors d'une redirection : `RedirectAttributes`
Comme mentionné, les attributs du modèle sont perdus lors d'une redirection car c'est une nouvelle requête. Comment alors passer des informations simples (comme un message de succès ou l'ID d'une entité créée) de la méthode qui initie la redirection à la méthode qui gère la nouvelle requête ? Spring MVC fournit l'interface RedirectAttributes.
Vous pouvez déclarer un paramètre de type RedirectAttributes dans votre méthode de handler qui effectue la redirection. Cet objet offre deux façons de passer des données :
addAttribute(String name, Object value): Ajoute l'attribut comme paramètre de requête URL si c'est un type simple, ou l'utilise pour remplir une variable de chemin (URI template variable) dans l'URL de redirection. Ces données sont visibles dans l'URL.addFlashAttribute(String name, Object value): Ajoute un attribut "flash". Ces attributs sont stockés temporairement (souvent dans la session HTTP) côté serveur et sont disponibles uniquement pour la requête qui suit immédiatement la redirection. Ils sont ensuite automatiquement supprimés. C'est idéal pour les messages de succès ou d'erreur temporaires qui ne doivent pas polluer l'URL.
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
@Controller
public class MessageController {
@PostMapping("/process")
public String processAction(@RequestParam String data, RedirectAttributes redirectAttributes) {
boolean success = // ... effectuer une action avec 'data'
Long generatedId = 123L;
if (success) {
// Ajoute un message de succès comme attribut Flash
redirectAttributes.addFlashAttribute("successMessage", "Action réussie pour " + data);
// Ajoute l'ID comme variable de chemin dans l'URL de redirection
redirectAttributes.addAttribute("id", generatedId);
return "redirect:/display/{id}"; // L'ID sera inséré dans {id}
} else {
redirectAttributes.addFlashAttribute("errorMessage", "Echec de l'action.");
// Ajoute 'data' comme paramètre de requête
redirectAttributes.addAttribute("originalData", data);
return "redirect:/showForm";
}
}
@GetMapping("/display/{id}")
public String displayResult(@PathVariable Long id, Model model) {
// Le Flash Attribute 'successMessage' est automatiquement ajouté au Model ici
// Vous pouvez y accéder directement dans la vue (ex: avec Thymeleaf ${successMessage})
System.out.println("Affichage du résultat pour ID: " + id);
if (model.containsAttribute("successMessage")) {
System.out.println("Message Flash reçu: " + model.getAttribute("successMessage"));
}
model.addAttribute("itemId", id);
return "displayView";
}
@GetMapping("/showForm")
public String showForm(@RequestParam(required = false) String originalData, Model model) {
// Le Flash Attribute 'errorMessage' est disponible dans le modèle
// Le paramètre 'originalData' est reçu de l'URL
if (model.containsAttribute("errorMessage")) {
System.out.println("Message Flash erreur reçu: " + model.getAttribute("errorMessage"));
}
System.out.println("Affichage du formulaire, data originale: " + originalData);
model.addAttribute("currentData", originalData);
return "formView";
}
}
Forward vs Redirect : Lequel choisir ?
Le choix dépend du contexte et de l'objectif :
- Utilisez
redirect::- Après une requête qui modifie des données (POST, PUT, DELETE) pour implémenter le pattern PRG et éviter les resoumissions.
- Pour naviguer vers une URL complètement différente, potentiellement gérée par un autre contrôleur ou même externe.
- Quand vous voulez que l'URL du navigateur reflète l'état final de l'application.
- Utilisez
forward::- Pour déléguer le traitement à une autre ressource serveur (vue ou handler) au sein de la même requête logique.
- Quand vous ne voulez pas que l'URL du navigateur change.
- Quand vous avez besoin de conserver les attributs de la requête originale pour la ressource cible.
- Attention : Ne pas utiliser
forward:après une requête POST qui modifie des données, car un rafraîchissement de la page par l'utilisateur pourrait rejouer l'action de modification.
En règle générale, la redirection est beaucoup plus couramment utilisée dans les flux d'applications web typiques, en particulier après des actions utilisateur, tandis que le forward est réservé à des cas d'usage plus spécifiques de traitement interne côté serveur.