Wofür wird der Parameter "next" in Express verwendet?

295

Angenommen, Sie haben einen einfachen Codeblock wie diesen:

app.get('/', function(req, res){
    res.send('Hello World');
});

Diese Funktion hat zwei Parameter reqund res, die die Anforderungs- bzw. Antwortobjekte darstellen.

Andererseits gibt es andere Funktionen, bei denen ein dritter Parameter aufgerufen wird next. Schauen wir uns zum Beispiel den folgenden Code an:

app.get('/users/:id?', function(req, res, next){ // Why do we need next?
    var id = req.params.id;
    if (id) {
        // do something
    } else {
        next(); // What is this doing?
    }
});

Ich kann nicht verstehen, worum es next()geht oder warum es verwendet wird. Wenn in diesem Beispiel keine ID vorhanden ist, was wird dann nexttatsächlich ausgeführt?

Menztrual
quelle
13
Next ermöglicht einfach dem nächsten Routenhandler in der Zeile, die Anforderung zu bearbeiten. In diesem Fall wird die Benutzer-ID wahrscheinlich verwendet res.send, um die Anforderung abzuschließen , wenn sie vorhanden ist . Wenn es nicht vorhanden ist, gibt es wahrscheinlich einen anderen Handler, der einen Fehler ausgibt und die Anforderung dann abschließt.
Dominic Barnes
1
Sie sagen also, ich habe ein app.post('/login',function(req,res))After- app.get('/users',function(req,res)) Call-Login, das die nächste Route in der Datei app.js ist, indem Sie next () aufrufen?
Menztrual
2
Nein, Sie sollten sich auf diesen Teil der Express.js- Dokumentation beziehen: expressjs.com/guide.html#passing-route control
Dominic Barnes
3
Grundsätzlich ist die nächste auszuführende Route eine andere, mit der die URL für die Anforderung übereinstimmt. Wenn in diesem Fall eine andere Route über registriert wurde app.get("/users"), wird sie ausgeführt, wenn der oben angegebene Handler das nächste Mal anruft.
Dominic Barnes
3
Weiter ist im Grunde nur ein Rückruf.
Jonathan Ong

Antworten:

266

Es übergibt die Kontrolle an die nächste passende Route. In dem Beispiel, das Sie beispielsweise idangeben, können Sie den Benutzer in der Datenbank nachschlagen, wenn ein angegeben wurde, und ihn zuweisen req.user.

Unten könnten Sie eine Route haben wie:

app.get('/users', function(req, res) {
  // check for and maybe do something with req.user
});

Da / users / 123 zuerst mit der Route in Ihrem Beispiel übereinstimmt, wird zuerst der Benutzer überprüft und gefunden 123. dann /userskann man etwas daraus machen.

Die Route Middleware ist meiner Meinung nach ein flexibleres und leistungsfähigeres Tool, da sie nicht auf einem bestimmten URI-Schema oder einer bestimmten Routenreihenfolge basiert. Ich würde das gezeigte Beispiel gerne Usersmodellieren , wenn ich ein asynchrones Modell annehme findOne():

function loadUser(req, res, next) {
  if (req.params.userId) {
    Users.findOne({ id: req.params.userId }, function(err, user) {
      if (err) {
        next(new Error("Couldn't find user: " + err));
        return;
      }

      req.user = user;
      next();
    });
  } else {
    next();
  }
}

// ...

app.get('/user/:userId', loadUser, function(req, res) {
  // do something with req.user
});

app.get('/users/:userId?', loadUser, function(req, res) {
  // if req.user was set, it's because userId was specified (and we found the user).
});

// Pretend there's a "loadItem()" which operates similarly, but with itemId.
app.get('/item/:itemId/addTo/:userId', loadItem, loadUser, function(req, res) {
  req.user.items.append(req.item.name);
});

In der Lage zu sein, den Fluss so zu steuern, ist ziemlich praktisch. Möglicherweise möchten Sie, dass bestimmte Seiten nur Benutzern mit einem Admin-Flag zur Verfügung stehen:

/**
 * Only allows the page to be accessed if the user is an admin.
 * Requires use of `loadUser` middleware.
 */
function requireAdmin(req, res, next) {
  if (!req.user || !req.user.admin) {
    next(new Error("Permission denied."));
    return;
  }

  next();
}

app.get('/top/secret', loadUser, requireAdmin, function(req, res) {
  res.send('blahblahblah');
});

Hoffe das hat dich inspiriert!

Asherah
quelle
Das könntest du sagen! Ich würde eher dazu neigen, solche Dinge mit Routen-Middleware zu tun , da sie die Logik nicht an eine bestimmte Reihenfolge von Routen oder bestimmte URI-Strukturen koppelt.
Asherah
5
warum manchmal kehren Sie als nächstes zurück (), aber manchmal nicht
John
6
@ John: Der Rückgabewert wird tatsächlich ignoriert. Ich möchte nur dorthin zurückkehren, um sicherzustellen, dass ich nicht next()wieder anrufe . Es wäre dasselbe, wenn ich es nur benutzen würde next(new Error(…)); return;.
Asherah
1
@ level0: Der Rückgabewert wird ignoriert. Sie können es als Abkürzung für betrachten next(new Error(…)); return;. Wenn wir einen Wert an übergeben next, wird dies einseitig als Fehler betrachtet . Ich habe nicht zu viel in den Express-Code geschaut, aber stöbern Sie herum und Sie werden finden, was Sie brauchen :)
Asherah
1
@ Level0: (Ich habe geändert return next(…);, next(…); return;damit es weniger verwirrend ist.)
Asherah
87

Ich hatte auch Probleme beim Verstehen von next (), aber das half

var app = require("express")();

app.get("/", function(httpRequest, httpResponse, next){
    httpResponse.write("Hello");
    next(); //remove this and see what happens 
});

app.get("/", function(httpRequest, httpResponse, next){
    httpResponse.write(" World !!!");
    httpResponse.end();
});

app.listen(8080);
Rajesk
quelle
3
Sehr prägnant! Vielen Dank! Aber wie stellen Sie sicher, dass der erste .getaufgerufen wird und nicht der zweite?
JohnnyQ
18
@ JohnnyQ Es wird von oben nach unten Ausführung sein
Tapash
59

Bevor Sie verstehen next, müssen Sie eine kleine Vorstellung vom Anforderungs-Antwort-Zyklus im Knoten haben, wenn auch nicht sehr detailliert. Es beginnt damit, dass Sie eine HTTP-Anfrage für eine bestimmte Ressource stellen, und endet, wenn Sie eine Antwort an den Benutzer zurücksenden, dh wenn Sie auf etwas wie res.send ('Hello World') stoßen.

Schauen wir uns ein sehr einfaches Beispiel an.

app.get('/hello', function (req, res, next) {
  res.send('USER')
})

Hier brauchen wir next () nicht, da resp.send den Zyklus beendet und die Steuerung an die Route Middleware zurückgibt.

Schauen wir uns nun ein anderes Beispiel an.

app.get('/hello', function (req, res, next) {
  res.send("Hello World !!!!");
});

app.get('/hello', function (req, res, next) {
  res.send("Hello Planet !!!!");
});

Hier haben wir 2 Middleware-Funktionen für den gleichen Pfad. Aber Sie werden immer die Antwort vom ersten bekommen. Da dies zuerst im Middleware-Stack bereitgestellt wird und res.send den Zyklus beendet.

Aber was ist, wenn wir immer nicht die "Hallo Welt !!!!" wollen? Antwort zurück. Für einige Bedingungen möchten wir vielleicht den "Hallo Planeten !!!!" Antwort. Lassen Sie uns den obigen Code ändern und sehen, was passiert.

app.get('/hello', function (req, res, next) {
  if(some condition){
    next();
    return;
  }
  res.send("Hello World !!!!");  
});

app.get('/hello', function (req, res, next) {
  res.send("Hello Planet !!!!");
});

Was ist nexthier los ? Und ja, Sie könnten Gusses haben. Wenn die Bedingung erfüllt ist, wird die erste Middleware-Funktion übersprungen und die nächste Middleware-Funktion aufgerufen, und Sie erhalten die "Hello Planet !!!!"Antwort.

Übergeben Sie das Steuerelement als Nächstes an die nächste Funktion im Middleware-Stack.

Was ist, wenn die erste Middleware-Funktion keine Antwort zurücksendet, sondern eine Logik ausführt und Sie die Antwort von der zweiten Middleware-Funktion zurückerhalten.

So etwas wie unten: -

app.get('/hello', function (req, res, next) {
  // Your piece of logic
  next();
});

app.get('/hello', function (req, res, next) {
  res.send("Hello !!!!");
});

In diesem Fall müssen beide Middleware-Funktionen aufgerufen werden. Der einzige Weg, um zur zweiten Middleware-Funktion zu gelangen, ist der Aufruf von next ().

Was ist, wenn Sie nicht den nächsten Anruf tätigen? Erwarten Sie nicht, dass die zweite Middleware-Funktion automatisch aufgerufen wird. Nach dem Aufrufen der ersten Funktion bleibt Ihre Anfrage hängen. Die zweite Funktion wird niemals aufgerufen und Sie erhalten keine Antwort zurück.

Mav55
quelle
Funktioniert also next()wie ein gotomit einem fest verdrahteten Etikett? Das heißt, in Ihrem dritten Snippet würde, sobald Sie anrufen next(), res.send("Hello World !!!!"); nie ausgeführt werden? Ich bemerkte, dass @Ashe immer einen return;After Call hatte next, der Code im selben Ausführungsbaum hatte ... Ich schätze, ich konnte immer nur Express einchecken, oder?
Ruffin
@ Ruffin Ja, du kannst dir vorstellen, wie es mit einem Goto weitergeht. Aber als nächstes weiß er, wohin er gehen soll, im Gegensatz zu goto, für das ein Etikett erforderlich ist. Next übergibt die Steuerung an die nächste Middleware-Funktion. Sie können auch "next" benennen, was Sie möchten. Es ist nur ein Etikett hier. Aber die beste Vorgehensweise ist, den Namen 'next' zu verwenden
Mav55
3
Okay, sieht so aus, als ob das nicht korrekt ist. Ich habe den Code ( Pastebin hier ) ausprobiert und der Code nach dem next()Aufruf wird aufgerufen . In diesem Fall past the next() callwird in die Konsole geschrieben, und dann erhalte ich eine Error: Can't set headers after they are sent.Fehlermeldung, wie die zweite res.sendaufgerufen wird, wenn auch erfolglos. Der Code-Fluss kehrt nach dem next()Aufruf zurück, was @ Ashe's returns(oder eine andere Logikverwaltung) wichtig macht.
Ruffin
4
@ Ruffin, ja du hast recht. Nach dem benötigen wir eine return-Anweisung next(), um die Ausführung der verbleibenden Anweisungen zu überspringen. Vielen Dank für den Hinweis.
Mav55
1
Vielen Dank, dass Sie tatsächlich erklärt haben, was "Middleware" ist / tut, mit klaren Beispielen und nicht nur die Dokumentation nachzeichnen. Dies war die einzige Antwort, die tatsächlich etwas klares darüber aussagt, was passiert, warum und wie.
mc01
11

Mit Next wird die Steuerung an die nächste Middleware-Funktion übergeben. Wenn nicht, bleibt die Anfrage hängen oder offen.

Ronique Ricketts
quelle
6
Ich bin nicht sicher, was diese Antwort zu einer fast sieben Jahre alten Frage
hinzufügt
Obwohl kurz, führte mich dieser Kommentar zu folgendem: expressjs.com/en/guide/writing-middleware.html
hmak
@mherzig, es ist ein Liner und deckt alles ab
M. Gopal
5

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. Die next () -Funktion kann beliebig benannt werden, wird jedoch konventionell immer als "next" bezeichnet.

Binayak Gouri Shankar
quelle
2

Durch Ausführen der nextFunktion wird der Server benachrichtigt, dass Sie mit diesem Middleware-Schritt fertig sind, und er kann den nächsten Schritt in der Kette ausführen.

AGrush
quelle