Aller au contenu

Exercice 3 - Amélioration de l'application⚓︎

1. Développement de la V2.0⚓︎

Nous allons donc maintenant rajouter l'implémentation de la méthode doPost dans notre servlet pour gérer cette requête :

  1. Dans la classe StoreServlet, ouvrir le menu :material-mouse:Sources > Override/Implement Methods..., et surcharger la méthode doPost de HttpServlet.

    Astuce : surcharge rapide

    Pour surcharger une méthode, on peut également écrire le début de son nom, ici le début de doPost, et appuyer sur CtrlEntrée pour faire de l'autocomplétion. On obtient le même résultat, mais plus rapidement.

  2. Comme pour la méthode doGet, renommer les paramètres en request et response respectivement.

  3. Dans cette méthode, nous allons récupérer les données saisies par l'utilisateur depuis le formulaire HTML. Ces données sont en paramètre de la requête HTTP, que la méthode utilisée soit GET ou POST. Nous allons donc faire request.getParameter(<attribut_name>).

    ☕ Code Java - Méthode doPost - 1/4
    1
    2
    3
    String penNumber = request.getParameter("penNb");//(1)!
    String feltNumber = ...;
    String rubberNumber = ...;
    
    1. Le nom du paramètre, ici penNb, doit correspondre à l'attribut name de la balise <input type="*"> du formulaire HTML.
  4. Ces données saisies par l'utilisateur doivent être "envoyées" au modèle. Dans cette méthode, on rajoute donc :

    ☕ Code Java - Méthode doPost - 2/4
    1
    2
    3
    4
    5
    CartBean cart = new CartBean();
    
    cart.setPenNumber(Integer.parseInt(penNumber));//(1)!
    ...
    ...
    
    1. Les objets récupérés en paramètre de la requête sont des String. Ici, il faut donc le transformer en Integer.
  5. Ensuite, on appelle la couche métier, pour mettre à jour les données du panier (les 3 RG) en fonction de ce que l'utilisateur a sélectionné. Pour cela, il va nous falloir une instance de la classe implémentant l'interface StoreBusiness. On pourrait l'instancier ici, mais on aurait alors une nouvelle instance à chaque appel de la Servlet, ce qui serait inutile. On va donc instancier cette classe une seule fois, à l'initialisation de la Servlet. La méthode void init() throws ServletException sert à cela.

    Dans la classe StoreServlet, ajouter une propriété privée, qui ne sera instanciée qu'une seule fois :

    ☕ Code Java
    private StoreBusiness storeBusiness;
    
    @Override
    public void init() throws ServletException {
        this.storeBusiness = new StoreBusinessImpl();//(1)!
    }
    
    1. C'est ici qu'on indique quelle classe implémente notre interface.

      Pour l'instant, nous indiquons "en dur" quelle classe implémente l'interface. Nous verrons bientôt comment indiquer cela dans un fichier de configuration, afin de pouvoir changer d'implémentation sans avoir à toucher au code.

    Enfin, dans la méthode doPost, on peut maintenant appeler la couche métier :

    ☕ Code Java - Méthode doPost - 3/4
    1
    cart = this.storeBusiness.computePrices(cart);
    
  6. Enfin, on redirige l'utilisateur vers la bonne vue :

    ☕ Code Java - Méthode doPost - 4/4
    1
    2
    RequestDispatcher dispatcher = request.getRequestDispatcher("store.jsp");
    dispatcher.forward(request, response);
    

2. Test de la V2.0⚓︎

En testant à nouveau (pour cela, il suffit de redémarrer le serveur), on constate maintenant :

  1. Qu'au premier affichage, le nombre de chaque article est vide, et qu'il y a une erreur si on ne met pas des nombres partout ...
  2. Qu'il ne se passe rien si on saisit des nombres partout et qu'on cliquer sur Valider ...

3. Développement de la V2.1⚓︎

Nous allons commencer par traiter le deuxième problème.

Le comportement observé est en fait normal. Le modèle (ici, le bean CartBean) n'est pas "envoyé" à la vue par le contrôleur.
➡️ Pour cela, nous allons l'ajouter dans la requête. À la fin de la méthode doPost de la servlet, juste avant l'appel du RequestDispatcher, il faut ajouter :

☕ Code Java - StoreServlet.doPost
1
request.setAttribute("USER_CART", cart);//(1)!
  1. setAttribute permet d'ajouter un attribut à la requête (à ne pas confondre avec un paramètre).

    Le premier paramètre, "USER_CART" est la clef : c'est ce qui va nous permettre de récupérer le bean dans la vue.

    Le deuxième paramètre, cart, est le bean.

Le bean est maintenant envoyé à la vue, il faut le récupérer avec le méthode HttpRequest.getAttribute(). On rajoute le code correspondant juste avant la balise <!DOCTYPE html> :

Java Server Page
1
2
3
<% //(1)!
CartBean cart = (CartBean) request.getAttribute("USER_CART");
%>
  1. Les balises <% %> permettent d'ajouter des scriptlet, c'est-à-dire du code Java à l'intérieur du code HTML.

La classe Java CartBean doit être importée dans la JSP. Il suffit d'utiliser l'auto-complétion (CtrlSpace), lorsqu'on écrit CartBean, et l'import va être ajouté automatiquement tout en haut du fichier :

Java Server Page
1
<%@ page import="fr.univtours.polytech.boutique.model.CartBean"%> <!--(1)!-->
  1. Ceci s'appelle une directive.

On peut maintenant afficher les informations présentes dans le bean et calculées par la couche métier. Nous allons modifier la partie "Récapitulatif du panier" en ajoutant les appels au bean :

Java Server Page
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<fieldset>
    <legend>Récapitulatif du panier</legend>
    <table>
        <tr>
            <td>Panier :</td>
            <td><%=cart.getCartPrice()%></td><!--(1)!-->
        </tr>
        <tr>
            <td>Frais livraison :</td>
            <td><%=cart.getShippingCost()%></td>
        </tr>
        <tr>
            <td>Prix total :</td>
            <td><%=cart.getTotalPrice()%></td>
        </tr>
    </table>
</fieldset>
  1. Le code Java entre <%= %> s'appelle une expression. Cela permet d'afficher la valeur de l'expression.

    Le code <%=variable %> est équivalent à <% out.println(variable) %>, il affiche le contenu de variable.

4. Test de la V2.1⚓︎

Si on teste à nouveau notre application, on a maintenant une erreur au premier affichage 😫 :

Erreur de la V1.1

Cela vient du fait qu'au premier affichage, dans le scriptlet dans la JSP, on récupère dans la requête un objet que nous n'avons pas encore placé ... On obtient donc un null, d'où l'erreur :
java.lang.NullPointerException: Cannot invoke "fr.univtours.polytech.boutique.model.CartBean.getCartPrice()" because "cart" is null

5. Développement de la V2.2⚓︎

Il y a plusieurs façons de gérer cette erreur. Ce que nous allons faire, c'est de placer un objet CartBean vide (mais pas null) dans la requête au premier affichage, c'est-à-dire dans la méthode doGet. On a donc maintenant :

☕ Code Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {

    CartBean cart = new CartBean();//(1)!
    request.setAttribute("USER_CART", cart);//(2)!

    RequestDispatcher dispatcher = request.getRequestDispatcher("store.jsp");
    dispatcher.forward(request, response);
}
  1. On créée un objet vide, mais pas null.
  2. Il y a maintenant un objet dans la requête au premier affichage.

6. Test de la V2.2⚓︎

Le premier affichage fonctionne désormais. Il nous reste à mieux initialiser notre formulaire, notamment pour ne plus avoir d'erreur lorsqu'on clique sur Valider sans avoir changé les valeurs par défaut :

Affichage de la V1.2

7. Développement de la V2.3⚓︎

Le problème vient du fait qu'on "caste" une chaîne de caractère vide en Integer ... Une façon de ne plus avoir ce problème est d'initialiser tous les champs du bean CartBean à 0 (on supprime ainsi également l'affichage des null dans la partie panier) :

☕ Code Java - classe CartBean
1
2
3
4
5
6
private Integer penNumber = 0;
private Integer feltNumber = 0;
private Integer rubberNumber = 0;
private Double cartPrice = 0D;
private Double shippingCost = 0D;
private Double totalPrice = 0D;

Enfin, dans la vue, il faut récupérer les nombres d'articles sélectionnés. Pour cela, il faut ajouter l'attribut value aux balises HTML <input type="number">. Cela permet d'avoir une valeur par défaut, qui va être celle stockée dans le bean présent dans la requête. Dans 📄store.jsp, la partie Liste des produits devient donc :

📄 store.jsp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<form action="controller" method="post">
    <fieldset>
        <legend>Liste des produits</legend>
        <table>
            <tr>
                <td>Stylo</td>
                <td><input type="number" name="penNb" value="<%=cart.getPenNumber()%>"/>
                <td></td>
            </tr>
            <tr>
                <td>Feutre</td>
                <td><input type="number" name="feltNb" value="<%=cart.getFeltNumber()%>"/>
                <td></td>
            </tr>
            <tr>
                <td>Gomme</td>
                <td><input type="number" name="rubberNb" value="<%=cart.getRubberNumber()%>"/>
                <td></td>
            </tr>
        </table>
    </fieldset>
    <input type="submit" value="Valider"/>
</form>