Pour que notre middleware de téléchargement de fichiers fonctionne sur nos routes, nous devrons modifier ces dernières, car le format d'une requête contenant un fichier du front-end est différent.
Nous en profiterons aussi pour implémenter les règles de sécurité. Elles nous permettront de vérifier que la personne souhaitant modifier l’objet est celle qui l’a créé ou de récupérer l’ID de l’utilisateur à partir du token d’authentification.
Modifiez la route POST
Tout d'abord, ajoutons notre middleware multer
à notre route POST dans notre routeur stuff
:
const express = require('express');
const router = express.Router();
const auth = require('../middleware/auth');
const multer = require('../middleware/multer-config');
const stuffCtrl = require('../controllers/stuff');
router.get('/', auth, stuffCtrl.getAllThings);
router.post('/', auth, multer, stuffCtrl.createThing);
router.get('/:id', auth, stuffCtrl.getOneThing);
router.put('/:id', auth, stuffCtrl.modifyThing);
router.delete('/:id', auth, stuffCtrl.deleteThing);
module.exports = router;
Pour gérer correctement la nouvelle requête entrante, nous devons mettre à jour notre contrôleur :
exports.createThing = (req, res, next) => {
const thingObject = JSON.parse(req.body.thing);
delete thingObject._id;
delete thingObject._userId;
const thing = new Thing({
...thingObject,
userId: req.auth.userId,
imageUrl: `${req.protocol}://${req.get('host')}/images/${req.file.filename}`
});
thing.save()
.then(() => { res.status(201).json({message: 'Objet enregistré !'})})
.catch(error => { res.status(400).json( { error })})
};
Que fait le code ci-dessus ?
Pour ajouter un fichier à la requête, le front-end doit envoyer les données de la requête sous la forme form-data et non sous forme de JSON. Le corps de la requête contient une chaîne
thing
, qui est simplement un objetThing
converti en chaîne. Nous devons donc l'analyser à l'aide deJSON.parse()
pour obtenir un objet utilisable.Nous supprimons le champ
_userId
de la requête envoyée par le client car nous ne devons pas lui faire confiance (rien ne l’empêcherait de nous passer le userId d’une autre personne). Nous le remplaçons en base de données par le_userId
extrait du token par le middleware d’authentification.Nous devons également résoudre l'URL complète de notre image, car
req.file.filename
ne contient que le segmentfilename
. Nous utilisonsreq.protocol
pour obtenir le premier segment (dans notre cas'http'
). Nous ajoutons'://'
, puis utilisonsreq.get('host')
pour résoudre l'hôte du serveur (ici,'localhost:3000'
). Nous ajoutons finalement'/images/'
et le nom de fichier pour compléter notre URL.
En fait, nous effectuons une demande GET vers http://localhost:3000/images/<image-name>.jpg
. Cela semble simple, mais n'oubliez pas que notre application s'exécute sur localhost:3000
et nous ne lui avons pas indiqué comment répondre aux requêtes transmises à cette route : elle renvoie donc une erreur 404. Pour remédier à cela, nous devons indiquer à notre app.js
comment traiter les requêtes vers la route /image
, en rendant notre dossier images
statique.
Il nous faudra une nouvelle importation dans app.js
pour accéder au path de notre serveur :
const path = require('path');
De plus, nous ajoutons le gestionnaire de routage suivant juste au-dessus de nos routes actuelles :
app.use('/images', express.static(path.join(__dirname, 'images')));
Cela indique à Express qu'il faut gérer la ressource images
de manière statique (un sous-répertoire de notre répertoire de base, __dirname
) à chaque fois qu'elle reçoit une requête vers la route /images
. Enregistrez et actualisez l'application dans le navigateur. Désormais, tout devrait fonctionner correctement. Et maintenant, occupons-nous de la route PUT !
Modifiez la route PUT
La modification de notre route PUT est sensiblement plus compliquée, car nous devons prendre en compte deux possibilités : l'utilisateur a mis à jour l'image ou pas. Dans le premier cas, nous recevrons l'élément form-data et le fichier. Dans le second cas, nous recevrons uniquement les données JSON.
Il faut aussi nous assurer que la personne demandant la modification de l’objet est la propriétaire de celui-ci.
Tout d'abord, ajoutons multer
comme middleware à notre route PUT :
const express = require('express');
const router = express.Router();
const auth = require('../middleware/auth');
const multer = require('../middleware/multer-config');
const stuffCtrl = require('../controllers/stuff');
router.get('/', auth, stuffCtrl.getAllThings);
router.post('/', auth, multer, stuffCtrl.createThing);
router.get('/:id', auth, stuffCtrl.getOneThing);
router.put('/:id', auth, multer, stuffCtrl.modifyThing);
router.delete('/:id', auth, stuffCtrl.deleteThing);
module.exports = router;
À présent, nous devons modifier notre fonction modifyThing()
pour voir si nous avons reçu ou non un nouveau fichier et répondre en conséquence :
exports.modifyThing = (req, res, next) => {
const thingObject = req.file ? {
...JSON.parse(req.body.thing),
imageUrl: `${req.protocol}://${req.get('host')}/images/${req.file.filename}`
} : { ...req.body };
delete thingObject._userId;
Thing.findOne({_id: req.params.id})
.then((thing) => {
if (thing.userId != req.auth.userId) {
res.status(401).json({ message : 'Not authorized'});
} else {
Thing.updateOne({ _id: req.params.id}, { ...thingObject, _id: req.params.id})
.then(() => res.status(200).json({message : 'Objet modifié!'}))
.catch(error => res.status(401).json({ error }));
}
})
.catch((error) => {
res.status(400).json({ error });
});
};
Dans cette version modifiée de la fonction, on crée un objet thingObject
qui regarde si req.file
existe ou non. S'il existe, on traite la nouvelle image ; s'il n'existe pas, on traite simplement l'objet entrant. On crée ensuite une instance Thing
à partir de thingObject
, puis on effectue la modification. Nous avons auparavant, comme pour la route POST, supprimé le champ _userId
envoyé par le client afin d’éviter de changer son propriétaire et nous avons vérifié que le requérant est bien le propriétaire de l’objet.
Félicitations ! Notre application gère correctement les téléchargements de fichiers lorsque nous mettons de nouveaux articles en vente et lorsque nous modifions les articles existants.
En résumé
JSON.parse()
transforme un objet stringifié en Object JavaScript exploitable.Vous aurez besoin de
req.protocol
et dereq.get('host')
, connectés par'://'
et suivis dereq.file.filename
, pour reconstruire l'URL complète du fichier enregistré.Configurez votre serveur pour renvoyer des fichiers statiques pour une route donnée avec
express.static()
etpath.join()
.
Dans le prochain chapitre, nous verrons comment développer la fonction delete du back-end !