• 10 heures
  • Facile

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 03/09/2019

Interrogez l'utilisateur avec les formulaires

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Dans ce chapitre, je vais vous montrer comment interroger l'utilisateur avec Struts notamment en créant des formulaires. Pour cela, je vous propose d'implémenter un cas d'utilisation assez simple : créer un nouveau projet.

La création d'un projet va se passer ainsi :

  1. L'utilisateur entre sur une page avec un formulaire pour renseigner les informations du projet à créer.

  2. Il soumet le formulaire.

  3. L'action vérifie les informations du formulaire.

    • Si tout est correct, le projet est créé et l'utilisateur est renvoyé vers la page de détail du projet nouvellement crée où un message d'information lui indique que le projet à bien été ajouté.

    • Si le formulaire comporte des erreurs, l'utilisateur reste sur la page de création et les messages d'erreur sont affichés.

    • Si une erreur technique survient, la page d'erreur globale est affichée (global result"error").

Configuration du mapping Struts

La cinématique générale étant posée, je vais commencer par configurer le mapping Struts. J'ajoute une nouvelle action projet_new :

<!-- Actions permettant de créer un projet -->
<action name="projet_new"
        class="org.example.demo.ticket.webapp.action.GestionProjetAction"
        method="doCreate">
    <result name="input">/jsp/projet/new.jsp</result>
    <result name="success" type="redirectAction">
        <param name="actionName">projet_detail</param>
        <param name="id">${projet.id}</param>
    </result>
</action>

Cette action est implémentée par la méthode doCreate de la classe org.example.demo.ticket.webapp.action.GestionProjetAction.

Elle présente 2 résulats :

  • input : résultat affichant la JSP /jsp/projet/new.jsp (JSP contenant le formulaire de création de projet). Ce résulat est le résultat renvoyé à l'entrée dans l'action ou si le formulaire envoyé contient des éléments invalides.

  • success : résulat renvoyé si le projet a été ajouté avec succès. Il s'agit ici d'un résultat particulier : il est de type redirectAction. Cela signifie que l'utilisateur sera redirigé vers une autre action Struts (via un code de réponse HTTP 302).

Conserver les messages et les erreurs à travers une redirection

Étant donné qu'en cas de succès, le result est une redirection, si on ajoute, dans l'action de création, un message d'information indiquant que l'ajout de projet s'est bien passé, ce message ne faisant partie que du contexte de la requête, celui-ci sera perdu lors de la redirection.

Pour résoudre ce problème, Struts fournis un interceptor (store) permettant, en cas de redirection, de stocker les messages en session et de les restaurer lors de la redirection.

J'ajoute donc cet interceptor à la pile par défaut :

<!-- Action affichant le détail d'un projet -->
<action name="projet_detail"
        class="org.example.demo.ticket.webapp.action.GestionProjetAction"
        method="doDetail">

    <interceptor-ref name="store">
        <param name="operationMode">RETRIEVE</param>
    </interceptor-ref>
    <interceptor-ref name="defaultStack" />

    <result>/jsp/projet/detail.jsp</result>
</action>

<!-- Actions permettant de créer un projet -->
<action name="projet_new"
        class="org.example.demo.ticket.webapp.action.GestionProjetAction"
        method="doCreate">

    <interceptor-ref name="store">
        <param name="operationMode">STORE</param>
    </interceptor-ref>
    <interceptor-ref name="defaultStack" />

    <result name="input">/jsp/projet/new.jsp</result>
    <result name="success" type="redirectAction">
        <param name="actionName">projet_detail</param>
        <param name="id">${projet.id}</param>
    </result>
</action>

La vue

Adaptation de la page de détail d'un projet

Comme je veux afficher message d'information d'ajout de projet sur la page de détail, j'ajoute le tag <s:actionmessage> dans la JSP de détail (src/main.webapp/jsp/projet/detail.jsp) :

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>

<!DOCTYPE html>
<html>
<head>
    <%@ include file="../_include/head.jsp"%>
</head>

<body>
    <s:actionmessage />

    <h2>Détail du projet</h2>
    ...
</body>
</html>

Création de la page d'ajout de projet

Je crée un fichier JSP (src/main.webapp/jsp/projet/new.jsp) pour la page contenant le formulaire d'ajout de projet.

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>

<!DOCTYPE html>
<html>
<head>
    <%@ include file="../_include/head.jsp" %>
</head>

<body>
    <s:actionerror/>
    <s:actionmessage/>

    <h2>Création d'un projet</h2>

    <s:form action="projet_new">
        <s:textfield name="projet.nom" label="Nom" requiredLabel="true" />
        <s:select name="projet.responsable.id" label="Responsable"
                  list="listUtilisateur" listKey="id" listValue="prenom"
                  emptyOption="true"
                  requiredLabel="true"/>

        <s:checkbox name="projet.cloture" label="Cloturé"/>

        <s:submit value="OK"/>
    </s:form>
</body>
</html>

Le formulaire est créé avec le tag <s:form> dans lequel je précise l'action à exécuter lors du submit du formulaire. Cela se fait via l'attribut action et en mettant le nom de l'action configurée dans le mapping Struts (ici "projet_new").

Ensuite, j'ai ajouté des champs à l'intérieur du formulaire :

  • un champ texte (tag <s:textfield>) qui assignera l'attribut nom de l'attribut projet de la classe d'action (name="projet.nom"). L'étiquette de ce champ est Nom (label="Nom"). Le champ est marqué comme requis (requiredLabel="true").

  • une liste de sélection (tag <s:select>) qui assignera l'attribut id de l'attribut responsable de l'attribut projet de la classe d'action (name="projet.responsable.id"). L'étiquette de ce champ est Responsable (label="Responsable"). La liste de sélection est peuplée à partir de l'attribut listUtilisateur de la classe d'action (list="listUtilisateur"). Chaque option de la liste de sélection aura :

    • pour valeur l'attribut id des éléments de la liste (listKey="id") ;

    • pour affichage l'attribut prenom des éléments de la liste (listValue="prenom") ; le champ est marqué comme requis (requiredLabel="true").

  • Une case à cocher (tag <s:checkbox>) qui assignera l'attribut cloture de l'attribut projet de la classe d'action (name="projet.cloture").

  • Un bouton de submit du formulaire (tag <s:submit>) avec comme texte OK (value="OK")

La classe d'action GestionProjetAction

À la validation du formulaire, Struts va instancier un nouveau projet et l'affecter à l'attribut projet de la classe d'action. Il va ensuite affecter les attributs du projet avec les données du formulaire. Pour le champ projet.responsable.id, Struts va instancier un nouvel Utilisateur, l'affecter à l'attribut responsable du projet et va affecter l'attribut id du responsable.

Je complète la classe GestionProjetAction pour avoir :

  • un attribut projet avec un getter et un setter : permettra de recueillir les informations du formulaire ;

  • un attribut listUtilisateur avec un getter : pour peupler la liste de sélection du responsable dans la JSP new.jsp ;

  • la méthode doCreate associée à l'action projet_new dans la configuration de Struts.

package org.example.demo.ticket.webapp.action;

import java.util.Collections;
import java.util.Date;
import java.util.List;

import com.opensymphony.xwork2.ActionSupport;

import org.example.demo.ticket.model.bean.projet.Projet;
import org.example.demo.ticket.model.bean.utilisateur.Utilisateur;
import org.example.demo.ticket.model.exception.FunctionalException;
import org.example.demo.ticket.model.exception.NotFoundException;
import org.example.demo.ticket.model.exception.TechnicalException;
import org.example.demo.ticket.webapp.WebappHelper;


/**
 * Action de gestion des {@link Projet}
 */
public class GestionProjetAction extends ActionSupport {


    // ==================== Attributs ====================
    ...
    // ----- Eléments en sortie
    private List<Utilisateur> listUtilisateur;

    // ----- Eléments en entrée et sortie
    private Projet projet;


    // ==================== Getters/Setters ====================
    public Projet getProjet() {
        return projet;
    }
    public void setProjet(Projet pProjet) {
        projet = pProjet;
    }
    public List<Utilisateur> getListUtilisateur() {
        return listUtilisateur;
    }
    ...


    // ==================== Méthodes ====================
    /**
     * Action permettant de créer un nouveau {@link Projet}
     * @return input / success / error
     */
    public String doCreate() {
        // Si (this.projet == null) c'est que l'on entre dans l'ajout de projet
        // Sinon, c'est que l'on vient de valider le formulaire d'ajout

        // Par défaut, le result est "input"
        String vResult = ActionSupport.INPUT;

        // ===== Validation de l'ajout de projet (projet != null)
        if (this.projet != null) {
            // Récupération du responsable
            if (this.projet.getResponsable() == null
                || this.projet.getResponsable().getId() == null) {
                this.addFieldError("projet.responsable.id", "ne doit pas être vide");
            } else {
                try {
                    Utilisateur vResponsable
                        = WebappHelper.getManagerFactory().getUtilisateurManager()
                                      .getUtilisateur(this.projet.getResponsable().getId());
                    this.projet.setResponsable(vResponsable);
                } catch (NotFoundException pEx) {
                    this.addFieldError("projet.responsable.id", pEx.getMessage());
                }
            }
            // Date de création
            this.projet.setDateCreation(new Date());

            // Si pas d'erreur, ajout du projet...
            if (!this.hasErrors()) {
                try {
                    WebappHelper.getManagerFactory().getProjetManager().insertProjet(this.projet);
                    // Si ajout avec succès -> Result "success"
                    vResult = ActionSupport.SUCCESS;
                    this.addActionMessage("Projet ajouté avec succès");

                } catch (FunctionalException pEx) {
                    // Sur erreur fonctionnelle on reste sur la page de saisie
                    // et on affiche un message d'erreur
                    this.addActionError(pEx.getMessage());

                } catch (TechnicalException pEx) {
                    // Sur erreur technique on part sur le result "error"
                    this.addActionError(pEx.getMessage());
                    vResult = ActionSupport.ERROR;
                }
            }
        }

        // Si on doit aller sur le formulaire de saisie, il faut ajouter les info nécessaires
        if (vResult.equals(ActionSupport.INPUT)) {
            this.listUtilisateur = WebappHelper.getManagerFactory().getUtilisateurManager().getListUtilisateur();
        }

        return vResult;
    }

    ...
}

La méthode doCreate bien qu'un peu longue au premier abord, n'est en réalité pas très compliquée. Cette méthode est appelée à l'entrée dans l'ajout de projet et à la validation du formulaire. Pour distinguer les deux cas, je me base sur l'attribut projet :

  • à l'entrée dans l'ajout, aucun paramètre n'est passé, donc l'attribut projet est à null ;

  • lors de la soumission du formulaire, les champs projet.* du formulaire sont envoyés, donc l'attribut projet est peuplé par Struts.

Lors de la soumission :

  1. Je vérifie que l'identifiant du responsable est bien renseigné (sinon j'ajoute une erreur sur ce champ) et je recherche l'utilisateur correspondant pour l'affecter en tant que responsable du projet.

  2. Je mets la date courant en tant que date de création du projet.

  3. Si je n'ai relevé aucune erreur, j'ajoute alors le projet un message d'information et passe le result à "success".

Enfin, si le result est "input", il faut peupler les attributs de l'action nécessaires au formulaire, notamment la liste des utilisateurs pour la liste de sélection du responsable du projet.

Validation des informations en entrée

Avec ce que je viens de coder, quelques vérifications sont faites au niveau de l'action (id du responsable non vide, le responsable est un utilisateur existant...). De plus, Struts fait également quelques vérifications de base. Par exemple, si j'avais mis dans le formulaire le champ correspondant à la date de création du projet, au moment de peupler les attributs correspondant aux paramètres en entrée, Struts vérifie que la valeur de ces paramètres est compatible avec le type Java de l'attribut de destination. Si ce n'est pas le cas (comme une date au format invalide ou des lettres pour un attribut de type Integer), Struts ajoute une erreur sur le champ concerné (comme avec la méthode addFieldError).

Pour le reste des validations, étant donné qu'il s'agit de règles métier (attributs obligatoires, longueur maximum des textes...), cela est fait au niveau des couches business et model. J'utilise notamment l'API de validation de bean (JSR 303 et 349).

...
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

public class Projet {

    // ==================== Attributs ====================
    private Integer id;

    @NotNull
    @Size(min = 1, max = 100)
    private String nom;

    @NotNull
    private Date dateCreation;

    private Boolean cloture;

    @NotNull
    private Utilisateur responsable;

    ...
}

Résumé

Ce qu'il faut retenir :

  • Afin d'interroger l'utilisateur, vous créez des vues. Ici j'ai créé une JSP contenant un formulaire.

  • Pour créer facilement des formulaires avec Struts, vous pouvez utiliser :

    • le tag <s:form> pour le formulaire,

    • les tags <s:textfield><s:select><s:checkbox>... pour les champs et les boutons du formulaire,

    • cf. documentation des tags.

  • Les champs du formulaire sont envoyés via une requête HTTP (POST). Struts peuple alors les attributs de l'instance d'Action correspondant à la requête via les setters.

  • En cas de result de type redirectAction, ne pas oublier d'ajouter l'interceptorstore afin de conserver les éventuels messages d'information ou d'erreur à travers la redirection.

Dans le chapitre suivant, je vais vous montrer comment gérer la session utilisateur avec Struts.

Exemple de certificat de réussite
Exemple de certificat de réussite