J'ai un itinéraire simple comme celui-ci:
// This is just a health check const endpoint = (req, res, next) => { res.json({ status: 'ok', }); next(); }; router.get('/', endpoint);
J'obtiens Erreur: impossible de définir les en-têtes après leur envoi.
depuis express. C'est même si je n'ai aucun autre middleware possible à exécuter ensuite, ou si mon middleware next
ne fait littéralement rien et est une fonction vide.
Que me manque-t-il dans la manière dont le middleware et next
fonctionne?
3 Réponses :
Je pense que vous y avez répondu dans votre propre question: vous ne pouvez rien faire à une réponse une fois qu'elle a déjà été envoyée. Et si vous y réfléchissez, cela a du sens. Si next
est utilisé pour modifier la requête d'une manière ou d'une autre, pourquoi «agirait-il» encore comme si elle «fonctionnait» après que la requête ait déjà été envoyée?
Selon le Documents Express , voici comment ils utilisent next
dans un get
demande:
app.get('/example/b', function (req, res, next) { console.log('the response will be sent by the next function ...') next() }, function (req, res) { res.send('Hello from B!') })
J'espère prendre quelques données après coup et faire une demande xhr. Est-ce que cela compterait même pour manipuler la réponse? Je comprends que c'est une erreur si j'essaye d'appeler res.send ou quelque chose comme ça à nouveau, mais pas seulement une méthode vide. Mais tu dis que c'est comme ça que c'est censé être, hein?
Ahh! On dirait que vous essayez d'utiliser next
d'une manière pour laquelle il n'était pas destiné à être utilisé. Qu'entendez-vous par «prendre des données après coup»?
Cette question fait suite à stackoverflow.com/questions/56243480/... , où une réponse donnait l'impression que je pouvais le faire. Mon désir réel de pouvoir regarder soit: req.path & req.params
ou req.route
. La deuxième option ne semble pas faisable sans un piratage important. Je prendrais ces données pour définir le nom de transaction approprié dans la nouvelle relique
Si vous avez besoin de faire autre chose dans ce point final particulier, la bonne façon serait de le faire avant d'envoyer la réponse. C'est ainsi que fonctionne l'architecture d'Express.
Si vous voulez appliquer la même logique à chaque point de terminaison (cela ressemble à ça, puisque vous voulez vérifier req.path
, req.params
et / ou req.route
), vous pouvez utiliser un middleware pour écouter l'événement finish
d'express:
router.use((req, res, next) => { res.on('finish', function() { // extra logic here after sending response }); next(); }); const endpoint = (req, res, next) => { res.json({ status: 'ok', }); }; router.get('/', endpoint);
Savez-vous qu'il s'exécutera avant que la connexion ne soit coupée? c'est-à-dire que si je boucle vers un point final, la logique finish
se déclenche-t-elle avant que j'obtienne une réponse?
L'intergiciel de finition doit être exécuté après la coupure de la connexion. Par exemple, si vous lancez une erreur dans la ligne extra logic
, le client ne recevra aucune erreur.
Ouais, ce serait un problème pour moi étant donné que je veux utiliser ceci pour définir un nom de transaction pour New Relic basé sur le req.route.
Je vois. Le cycle de vie d'une requête dans express se termine par res.json ()
/ res.send ()
. Vous pouvez transmettre la réponse à un nouveau middleware final à req.response
et faire un peu de logique entre les deux.
Il peut y avoir quelques problèmes:
Vous avez un gestionnaire 404
à la fin (nous le faisons tous) et vous avez simplement négligé. Par exemple:
function test(x) { switch (x) { case 'Oranges': console.log('Orange juice'); break; case 'Mangoes': // fall through case 'Papayas': console.log('Pickles'); break; case 'Banana': console.log('Milkshake'); // forgot the break case 'Apple': console.log('Apple Pie') break; default: console.log('Sorry, no ' + x + '.'); } } test('Banana')
Cela entraînerait une erreur Erreur: Impossible de définir les en-têtes après leur envoi.
Parce que votre gestionnaire 404 envoie une réponse .
Ou Vous êtes tombé sur une caractéristique inconnue (Ou simplement, l'un de vos itinéraires ne fonctionne pas comme prévu, peut-être une erreur de conception). Considérez l'itinéraire suivant:
app.get('/path/:id', (req, res, next) => { res.json({ message: "Welcome!" }); next() // <-- calling next middleware }) app.get('/path/test', (req, res, next) => { res.json({ message: "Welcome!" }); })
L'appel de next
dans l'itinéraire / path /: id
entraînerait une erreur si votre demande le chemin est / path / test
(cela suggère en général une meilleure implémentation de routage)
Un moyen simple de comprendre le middleware
et le suivant
est lié au fonctionnement de l'instruction switch
:
app.get('/path', (req, res, next) => { res.json({ greet: "Welcome!" }) next() // <-- calling next middleware }) // set up a 404 handler at the end app.use((req, res) => { res.status(404).send('Opps! Not Found'); })
Appeler next
revient à passer
à une instruction switch
, exécutez également le cas suivant.
Ainsi, lorsque vous appelez next
dans une route et que la demande
continue pour correspondre à une autre route
(une correspondance involontaire) où vous envoyez réponse, vous vous surprendrez à vous frapper le visage avec l'erreur.
La 404 était la réponse. J'ai router.get ('*')
pour gérer les 404, mais la même différence ici. Superbe capture.
PS: Je vais passer le 404 à un middleware et m'assurer qu'il fonctionne après ce que j'essaye de faire maintenant. Briser ça va arranger les choses