Wann wird next () verwendet und next () in Node.js zurückgegeben?

136

Szenario : Beachten Sie, dass der folgende Teil des Codes aus einer Knoten-Webanwendung stammt.

app.get('/users/:id?', function(req, res, next){
    var id = req.params.id;
    if (id) {
        // do something
    } else {
        next(); //or return next();
    }
});

Problem : Ich überprüfe, mit welchem ​​ich gerade next()oder gehen soll return next(). Der obige Beispielcode funktioniert für beide genau gleich und zeigte keinen Unterschied in der Ausführung.

Frage : Kann jemand Licht ins Dunkel bringen, wann next()und wann man es benutzt return next()und einen wichtigen Unterschied?

Amol M Kulkarni
quelle

Antworten:

141

Einige Leute schreiben immer, return next()um sicherzustellen, dass die Ausführung nach dem Auslösen des Rückrufs stoppt.

Wenn Sie dies nicht tun, riskieren Sie, den Rückruf ein zweites Mal später auszulösen, was normalerweise verheerende Folgen hat. Ihr Code ist in Ordnung, aber ich würde ihn wie folgt umschreiben:

app.get('/users/:id?', function(req, res, next){
    var id = req.params.id;

    if(!id)
        return next();

    // do something
});

Es erspart mir eine Einrückungsstufe, und wenn ich den Code später noch einmal lese, bin ich sicher, dass es keine Möglichkeit nextgibt, zweimal aufgerufen zu werden.

Laurent Perrin
quelle
2
Wäre etwas Ähnliches für res.redirect('/')vs. return res.redirect('/')in einer solchen Situation? Vielleicht ist es einfach besser, return immer vor res-Anweisungen zu schreiben, um Fehler beim Festlegen von Headern nach dem Senden zu vermeiden?
Adam D
184

Als Antwort von @Laurent Perrin:

Wenn Sie dies nicht tun, riskieren Sie, den Rückruf ein zweites Mal später auszulösen, was normalerweise verheerende Folgen hat

Ich gebe hier ein Beispiel, wenn Sie Middleware wie diese schreiben:

app.use((req, res, next) => {
  console.log('This is a middleware')
  next()
  console.log('This is first-half middleware')
})

app.use((req, res, next) => {
  console.log('This is second middleware')
  next()
})

app.use((req, res, next) => {
  console.log('This is third middleware')
  next()
})

Sie werden feststellen, dass die Ausgabe in der Konsole ist:

This is a middleware
This is second middleware
This is third middleware
This is first-half middleware

Das heißt, es wird der folgende Code next () ausgeführt, nachdem alle Middleware-Funktionen beendet wurden.

Wenn Sie jedoch verwenden return next(), wird der Rückruf sofort übersprungen und der unten return next()im Rückruf angegebene Code ist nicht erreichbar.

PJCHENder
quelle
29
Als Anfänger expressdieser Antwort wurden mir die Dinge klarer als die anderen Antworten. Daumen hoch!
Mandarin
1
Wäre etwas Ähnliches für res.redirect('/')vs. return res.redirect('/')in einer solchen Situation? Vielleicht ist es einfach besser, immer returnvor resAnweisungen zu schreiben , um Fehler beim Festlegen von Headern nach dem Senden zu vermeiden?
Adam D
1
Warum sollte ich nach next () Code schreiben? Ist es nicht offensichtlich, dass ich nichts tue, nachdem ich meine Aufgabe in einer Middleware erledigt habe? @PJCHENder
Imran Pollob
1
@ ImranPollob manchmal passieren Fehler. Wenn Sie viel Code schreiben, ifs / elses / etc. Sie können vergessen, `` `next ()`
Jone Polvora
46

next()ist Teil der Connect Middleware . Rückrufe für den Routerfluss kümmern sich nicht darum, ob Sie etwas von Ihren Funktionen zurückgeben, return next()und next(); return;sind im Grunde dasselbe.

Wenn Sie den Funktionsfluss stoppen möchten, können Sie next(err)Folgendes verwenden

app.get('/user/:id?', 
    function(req, res, next) { 
        console.log('function one');
        if ( !req.params.id ) 
            next('No ID'); // This will return error
        else   
            next(); // This will continue to function 2
    },
    function(req, res) { 
        console.log('function two'); 
    }
);

Ziemlich viel next()wird verwendet, um die Middleware Ihrer Anfragen zu erweitern.

drinchev
quelle
1
Können wir Parameter wie folgt senden : next('No ID')?
Amol M Kulkarni
7
next('No ID')sendet tatsächlich einen Fehler, der den Fluss unterbricht.
Drinchev
Verwenden Sie next (null, "somevalue"); Bei Tools wie async.waterfall wird der Wert an die nächste Funktion übergeben. Bei komplexen Serien von Interaktionen, die datengesteuert sind, übergebe ich normalerweise ein Kontextobjekt zwischen Funktionen. Auf diese Weise kann ich generische Funktionen erstellen, die von mehreren Endpunkten gemeinsam genutzt werden können, und den Datenfluss über Daten im Kontext steuern
Chad Wilson,
5
"also return next () und next (); return; ist im Grunde das gleiche." - genau das, was ich lesen musste. thx @drinchev
Nick Pineda
1
Ich beobachte das Gegenteil (beim Auslösen eines Fehlers): next (error) löst die nächste Middleware aus, führt aber weiterhin Code aus; return next (Fehler) führt die Ausführung nur zur nächsten Middleware. next (e) und return next (e) sind NICHT gleich.
Nickolodeon
0

Es ist am besten, es überhaupt nicht zu benutzen! Ich erkläre, und das erkläre ich auch.

Die next () -Funktion, die einen beliebigen Namen haben kann und gemäß Konvention auf next gesetzt wurde. Es hängt indirekt mit den Operationen (PUT, GET, DELETE, ...) zusammen, die beispielsweise im Allgemeinen auf derselben URI-Ressource ausgeführt werden/ user /: id

app.get('/user/:id', function (req,res,next)...)
app.put('/user/:id', function (req,res,next)...)
app.delete('/user/:id', function (req,res,next)...)
app.post('/user/', function ()...)

Wenn Sie sich nun app.get ansehen, verwenden app.put und app.delete dieselbe URL (/ user /: id). Das einzige, was sie unterscheidet, ist ihre Implementierung. Wenn die Anforderung gestellt wird (req) express, wird die Anforderung in app.get an die erste Stelle gesetzt. Wenn eine von Ihnen erstellte Validierung fehlschlägt, weil diese Anforderung nicht für diesen Controller gilt, wird die Anforderung an app.put übergeben, die die nächste Route in der Datei darstellt auf. Wie im folgenden Beispiel zu sehen.

    app.get('/user/:id', function (req,res,next){

    if(req.method === 'GET')
    //whatever you are going to do
    else
      return next() //it passes the request to app.put

    //Where would GET response 404 go, here? or in the next one. 
    // Will the GET answer be handled by a PUT? Something is wrong here.

   })
    app.put('/user/:id', function (req,res,next){

    if(req.method === 'PUT')
    //whatever you are going to do
    else
      return next()

   })

Das Problem besteht darin, dass Sie am Ende die Anforderung an alle Controller weitergeben, in der Hoffnung, dass es eine gibt, die durch die Validierung der Anforderung das tut, was Sie wollen. Am Ende erhalten alle Controller etwas, das nicht für sie ist :(.

Wie vermeide ich das Problem von next () ?

Die Antwort ist wirklich einfach.

1- Es sollte nur einen Uri geben , um eine Ressource zu identifizieren

http: // IpServidor / colection /: resource / colection /: resource Wenn Ihr URI länger ist, sollten Sie eine neue URL erstellen

Beispiel http: // IpServidor / users / pepe / contact / contacto1

2-Alle Operationen an dieser Ressource müssen unter Berücksichtigung der Idempotenz der Verben http (get, post, put, delete, ...) ausgeführt werden, damit der Aufruf einer URI wirklich nur eine Möglichkeit zum Aufrufen hat

POST http://IpServidor/users/  //create a pepe user 
GET http://IpServidor/users/pepe  //user pepe returns   
PUT http://IpServidor/users/pepe  //update the user pepe 
DELETE http://IpServidor/users/pepe  //remove the user pepe

Weitere Informationen [ https://docs.microsoft.com/es-es/azure/architecture/best-practices/api-design#organize-the-api-around-resources‹[1]

Mal sehen, den Code! Die konkrete Implementierung, durch die wir die Verwendung von next () vermeiden!

In der Datei index.js

//index.js the entry point to the application also caller app.js
const express = require('express');
const app = express();

const usersRoute = require('./src/route/usersRoute.js');

app.use('/users', usersRoute );

In der Datei usersRoute.js

    //usersRoute.js
    const express = require('express');
    const router = express.Router();

    const getUsersController = require('../Controllers/getUsersController.js');
    const deleteUsersController = require('../Controllers/deleteUsersController.js');

    router.use('/:name', function (req, res) //The path is in /users/:name
    {
    switch (req.method)
    {
    case 'DELETE':
      deleteUsersController(req, res);
      break;
    case 'PUT':
     // call to putUsersController(req, res);
     break;
    case 'GET':
     getUsersController(req, res);
     break;
    default:
     res.status(400).send('Bad request');
    } });

router.post('/',function (req,res) //The path is in /users/
{
    postUsersController(req, res);
});

module.exports = router;

Jetzt macht die Datei usersRoute.js das, was von einer Datei namens usersRoute erwartet wird, nämlich die Verwaltung der Routen des URI / users /

// Datei getUsersController.js

//getUsersController.js
    const findUser= require('../Aplication/findUser.js');
    const usersRepository = require('../Infraestructure/usersRepository.js');

    const getUsersController = async function (req, res)
    {

       try{
          const userName = req.params.name;
        //...
          res.status(200).send(user.propertys())

        }catch(findUserError){
           res.status(findUserError.code).send(findUserError.message)
        }
    }
   module.exports = getUsersController;

Auf diese Weise vermeiden Sie die Verwendung von next, entkoppeln den Code, gewinnen an Leistung, entwickeln SOLID, lassen die Tür für eine mögliche Migration zu Microservices offen und sind vor allem für einen Programmierer leicht lesbar.

David
quelle
2
Dies ist falsch. App.get wird nicht wie von Ihnen vorgeschlagen an app.put übergeben. Es werden nur übereinstimmende Anforderungen aufgerufen. Wenn die Methode GET lautet, wird nur die Middleware app.get aufgerufen. Die Middleware muss die Anforderungsmethode nicht überprüfen. Ihr Vorschlag ignoriert eine primäre Express-Funktion und implementiert stattdessen Ihr eigenes Routing. Außerdem geht Ihr Vorschlag davon aus, dass Ihre Route die einzige Middleware ist, die Sie verwenden werden, da sie nirgendwo weitergegeben wird.
Ravenex
Falsche Informationen, siehe Antworten oben.
DDiamond
-3

Nächster() :

Durch Aufrufen dieser Funktion wird die nächste Middleware-Funktion in der App aufgerufen. Die next () -Funktion ist kein Teil der Node.js- oder Express-API, sondern das dritte Argument, das an die Middleware-Funktion übergeben wird.

Manischer Sharma
quelle