Vérifier les identifiants et les paramètres de route
Objectif : Détecter un identifiant de commande invalide ou qui ne correspond à aucune ressource.
Une URL comme /api/commandes/abc ne désigne pas un identifiant numérique valide. Une URL comme /api/commandes/42 peut, elle, contenir un identifiant valide mais ne correspondre à aucune commande existante. Vous allez distinguer ces deux cas avant le traitement métier et conserver un format d’erreur uniforme.
La validation du corps de création, déjà mise en place, traite une autre partie de la requête. Ici, le middleware lit le paramètre de route id et vérifie d’abord son format. Le contrôleur cherche ensuite la commande ; s’il ne la trouve pas, il transmet une erreur indiquant une ressource absente.
Choisir le statut selon le cas
Un identifiant qui n’est pas un entier positif est une entrée invalide : répondez avec le statut 400 et le code d’erreur VALIDATION. Un entier positif qui ne désigne aucune commande correspond à une ressource absente : répondez avec le statut 404 et le code INTROUVABLE. Cette distinction aide le client HTTP à comprendre si la valeur envoyée doit être corrigée ou si la ressource demandée n’existe pas.
Créer le middleware de validation de l’identifiant
Complétez middlewares/valider.js sans retirer l’export validerCommande utilisé par la création. Le nouveau middleware convertit la chaîne reçue en nombre, puis la compare à sa représentation entière : cela écarte notamment les valeurs décimales et les chaînes qui contiennent des caractères supplémentaires. En cas de succès, il place l’identifiant numérique dans req.commandeId et laisse la requête poursuivre son chemin.
function validerCommande(req, res, next) {
const { produitId, quantite } = req.body;
if (
!req.body ||
typeof req.body !== 'object' ||
!Number.isInteger(produitId) ||
produitId <= 0 ||
!Number.isInteger(quantite) ||
quantite <= 0
) {
const erreur = new Error('Les données reçues sont invalides');
erreur.code = 'VALIDATION';
erreur.status = 400;
return next(erreur);
}
return next();
}
function validerIdCommande(req, res, next) {
const valeur = req.params.id;
const id = Number(valeur);
if (!Number.isInteger(id) || id <= 0 || String(id) !== valeur) {
const erreur = new Error('L’identifiant de commande est invalide');
erreur.code = 'VALIDATION';
erreur.status = 400;
return next(erreur);
}
req.commandeId = id;
return next();
}
module.exports = { validerCommande, validerIdCommande };Contrôler l’existence dans le contrôleur
Un identifiant bien formé n’assure pas que la commande existe. Faites cette vérification dans le contrôleur après l’appel au service : si celui-ci renvoie null, transmettez une erreur au gestionnaire central au lieu de construire une réponse de succès. L’exemple ci-dessous montre le fichier complet de contrôleur à cet état ; il conserve le traitement asynchrone de création et ajoute la lecture d’une commande par identifiant.
const commandesService = require('../services/commandes');
async function lister(req, res) {
const donnees = await commandesService.lister();
res.status(200).json({ donnees, erreur: null });
}
async function lire(req, res, next) {
const commande = await commandesService.trouverParId(req.commandeId);
if (!commande) {
const erreur = new Error('Commande introuvable');
erreur.code = 'INTROUVABLE';
erreur.status = 404;
return next(erreur);
}
return res.status(200).json({ donnees: commande, erreur: null });
}
async function creer(req, res) {
const commande = await commandesService.creer(req.body);
res.status(201).json({ donnees: commande, erreur: null });
}
module.exports = { lister, lire, creer };Le service doit fournir trouverParId et renvoyer null si aucune commande ne correspond. Si votre service ne possède pas encore cette opération, ajoutez-la en conservant son stockage actuel : le fragment suivant est autonome et ne remplace pas le reste du fichier. Dans le jeu de données en mémoire utilisé pour les premiers essais, une commande peut être recherchée par son identifiant numérique.
async function trouverParId(id) {
return commandes.find((commande) => commande.id === id) || null;
}
module.exports.trouverParId = trouverParId;Ce fragment suppose que le service dispose déjà d’un tableau nommé commandes ; gardez exactement le nom de votre stockage existant. La recherche est asynchrone pour respecter le contrat du service : une source de données différente pourra ensuite remplacer le tableau sans modifier le contrôleur.
Brancher le contrôle sur la route
Dans routes/commandes.js, placez validerIdCommande avant le contrôleur pour les chemins qui comportent :id. L’adaptateur ci-dessous transmet les rejets des contrôleurs asynchrones à Express, comme dans la leçon sur la centralisation des erreurs ; le middleware de validation, lui, appelle directement next en cas d’échec.
const express = require('express');
const commandesControleur = require('../controleurs/commandes');
const { validerCommande, validerIdCommande } = require('../middlewares/valider');
const router = express.Router();
function avecErreursAsync(fonction) {
return (req, res, next) => {
Promise.resolve(fonction(req, res, next)).catch(next);
};
}
router.get('/', avecErreursAsync(commandesControleur.lister));
router.get('/:id', validerIdCommande, avecErreursAsync(commandesControleur.lire));
router.post('/', validerCommande, avecErreursAsync(commandesControleur.creer));
module.exports = router;À retenir
- Validez le format de `:id` avant d’appeler le contrôleur : un identifiant invalide reçoit le statut 400.
- Cherchez l’identifiant valide dans le service ; une absence de résultat reçoit le statut 404.
- Transmettez les deux erreurs au middleware central pour conserver la forme JSON normalisée.
- Appliquez le middleware à chaque route de commande qui utilise un paramètre `:id`.
Dans la prochaine leçon, vous ajouterez des filtres aux listes de commandes à partir des paramètres de requête.