• 20 heures
  • Difficile

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 31/07/2019

Gérez les erreurs et validez les données

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

Gérez les erreurs

Pour introduire la problématique, lançons dans Postman une requête GET vers http://localhost:9090/Produits/42 en indiquant l'id d'un produit qui n'existe pas. Voici le résultat :

Figure 9
Requête vers un produit non existant

Étant donné que ce produit n'existe pas, cette requête ne fournit logiquement pas de réponse.

 Avez-vous prêté attention au code de statut retourné ?

Malgré l'absence de réponse, la requête a fourni un code de statut 200 OK. Celui-ci indique que la requête a réussi, c'est-à-dire que le serveur a pu être appelé et n'a retourné aucune erreur particulière. Or, ce comportement peut induire en erreur ou déboussoler tout service extérieur qui viendrait utiliser notre API : il n'y a aucun résultat, mais nous indiquons quand même que tout va bien ...

Pour remédier à ceci, nous allons générer une exception lorsqu'on ne trouve pas de produit :

//Récupérer un produit par son Id
   @GetMapping(value = "/Produits/{id}")

   public Product afficherUnProduit(@PathVariable int id) {

       Product produit = productDao.findById(id);

       if(produit==null) throw new ProduitIntrouvableException("Le produit avec l'id " + id + " est INTROUVABLE. Écran Bleu si je pouvais.")

       return produit;
   }

Si la variable produits est null, nous lançons une exception ProduitIntrouvableException. En effet, nous allons créer notre propre exception afin d'être le plus spécifique possible. 

Dans IntelliJ, ProduitIntrouvableException devrait être rouge car il n'y a pas de classe de ce nom. Cliquez sur ProduitIntrouvableException et une lampe rouge s'affiche à gauche, vous proposant de créer cette exception automatiquement.

Figure 10

Cliquez dessus : une nouvelle fenêtre s'ouvre et vous propose de choisir un package dans lequel placer cette classe. Vous allez changer de package et en indiquer un nouveau, exceptions  :

Figure 11

Un nouveau package est créé avec la nouvelle classe. Modifiez cette classe afin qu'elle hérite de RuntimeException. Passez ensuite le message reçu en argument via notre exception et vers la classe parent pour qu'elle l'intègre dans l'affichage de l'erreur. Voici notre exception finale :

package com.ecommerce.microcommerce.web.exceptions;

public class ProduitIntrouvableException extends RuntimeException {

    public ProduitIntrouvableException(String s) {
        super(s);
    }
}

Relancez maintenant votre requête GET sur Postman, vous obtenez donc une erreur en bonne et due forme avec notre petit message personnel :

Figure 12
Affichage du message d'erreur lié à l'exception

Tout ça est bien joli , mais il y a encore quelque-chose qui cloche. Est-ce que vous le voyez ? Il s'agit de l'erreur renvoyée : 500 Internal Server Error. Or, nous n'avons aucun problème de serveur, le problème est que la ressource cherchée est introuvable. Il nous faut donc retourner le code de statu adapté : 404 Not Found.

Nous allons donc modifier notre exception afin qu'elle renvoie le bon code. A cet effet, Spring fournit  l'annotation @ResponseStatus qui définit le code de statut associé à l'exception. Modifiez donc votre code comme suit : 

package com.ecommerce.microcommerce.web.exceptions;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(HttpStatus.NOT_FOUND)
public class ProduitIntrouvableException extends RuntimeException {

    public ProduitIntrouvableException(String s) {
        super(s);
    }
}

L'annotation désigne le code d'erreur à renvoyer parmi la liste de tous les codes possibles. Vous pouvez d'ailleurs afficher ces codes dans intelliJ grâce à l'assistant de code

Figure 13
Menu contextuel affichant les codes de status HTTP disponibles

Redémarrez et re-testez avec Postman :

Figure 14
Affichage du bon code de statut par Postman

Vous renvoyez ainsi un message d'erreur explicite avec le bon code http.

Validations

Jusqu'ici, lorsque nous créons un nouveau Produit grâce à la méthode ajouterProduit, nous  ne vérifions à aucun moment que les données sur le produit sont correctes. Par exemple, la longueur du nom du produit doit être supérieure à 3 caractères, et le prix ne doit jamais être défini à 0 (rien n'est gratuit :p ).

Spring propose de valider les données grâce à une dépendance importée avec notre starter de départ appelée hibernate-validator

Figure 15

Cette librairie va vous permettre de valider les données grâce à de simples annotations. Modifions la classe Product  en ajoutant les annotations @Lenght  et @Min , comme suit :

@Entity
//@JsonFilter("monFiltreDynamique")
public class Product {

    @Id
    @GeneratedValue
    private int id;

    @Length(min=3, max=20)
    private String nom;

    @Min(value = 1)
    private int prix;

    ...
    ...
  }

 Explications :

  • @Lenght : accepte en argument un minimum et un maximum, et vérifie donc si la longueur de la chaîne est conforme.

  • @Min : définit la valeur minimale.

Vous trouverez ici une liste non exhaustive des validations possibles et ici la documentation avec l'ensemble des possibilités.

Il nous faut également indiquer dans notre contrôleur que le produit reçu de l'utilisateur est à valider. Pour ce faire, il faut ajouter l'annotation @Valid, comme illustré ci-après :

public ResponseEntity<Void> ajouterProduit(@Valid @RequestBody Product product) {
  ....
}

Lancez un POST depuis Postman pour tester :

Figure 16
Affichage de l'erreur avec Postman

Vous voyez le bon code d'erreur : 400 Bad Request. De plus, le message d'erreur est très bon et bien détaillé : 

{
    "timestamp": 1516277681168,
    "status": 400,
    "error": "Bad Request",
    "exception": "org.springframework.web.bind.MethodArgumentNotValidException",
    "errors": [
        {
            "codes": [
                "Length.product.nom",
                "Length.nom",
                "Length.java.lang.String",
                "Length"
            ],
            "arguments": [
                {
                    "codes": [
                        "product.nom",
                        "nom"
                    ],
                    "arguments": null,
                    "defaultMessage": "nom",
                    "code": "nom"
                },
                20,
                3
            ],
            "defaultMessage": "la longueur doit être comprise entre 3 et 20 caractères",
            "objectName": "product",
            "field": "nom",
            "rejectedValue": "Poney en bois cracheur de feu",
            "bindingFailure": false,
            "code": "Length"
        },
        {
            "codes": [
                "Min.product.prix",
                "Min.prix",
                "Min.int",
                "Min"
            ],
            "arguments": [
                {
                    "codes": [
                        "product.prix",
                        "prix"
                    ],
                    "arguments": null,
                    "defaultMessage": "prix",
                    "code": "prix"
                },
                1
            ],
            "defaultMessage": "doit être au minimum égal à 1",
            "objectName": "product",
            "field": "prix",
            "rejectedValue": 0,
            "bindingFailure": false,
            "code": "Min"
        }
    ],
    "message": "Validation failed for object='product'. Error count: 2",
    "path": "/Produits"
}

Vous remarquerez que Validator pousse le vice jusqu'à créer un message d'erreur en français pour vous : "la longueur doit être comprise entre 3 et 20 caractères", n'est-ce pas fantastique ?

D'accord, mais si je souhaite écrire mes propres messages ?

Pour changer le message d'erreur, il suffit d'ajouter l'argument message à vos annotations :

@Length(min=3, max=20, message = "Nom trop long ou trop court. Et oui messages sont plus stylés que ceux de Spring")
   private String nom;

Testez avec Postman !

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