A cosa serve il parametro "next" utilizzato in Express?


295

Supponiamo di avere un semplice blocco di codice come questo:

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

Questa funzione ha due parametri reqe res, che rappresentano rispettivamente gli oggetti richiesta e risposta.

D'altra parte, ci sono altre funzioni con un terzo parametro chiamato next. Ad esempio, diamo un'occhiata al seguente codice:

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?
    }
});

Non riesco a capire quale next()sia lo scopo o perché venga utilizzato. In questo esempio, se ID non esiste, cosa sta nextrealmente facendo?


13
Next consente semplicemente al gestore del percorso successivo in linea di gestire la richiesta. In questo caso, se esiste l'ID utente, verrà probabilmente utilizzato res.sendper completare la richiesta. Se non esiste, è probabile che esista un altro gestore che emetterà un errore e completerà la richiesta.
Dominic Barnes,

1
quindi stai dicendo che ho un app.post('/login',function(req,res))dopo app.get('/users',function(req,res)) che chiamerà login come il prossimo percorso nel file app.js chiamando next ()?
Menztrual,

2
No, è necessario fare riferimento a questa parte della documentazione di Express.js: expressjs.com/guide.html#passing- controllo del percorso
Dominic Barnes,

3
Fondamentalmente, il prossimo percorso da eseguire sarà un altro che corrisponda all'URL della richiesta. In questo caso, se è stato registrato un altro percorso tramite app.get("/users"), verrà eseguito se il gestore sopra chiama.
Dominic Barnes,

3
Il prossimo è fondamentalmente solo un callback.
Jonathan Ong,

Risposte:


266

Passa il controllo al prossimo percorso corrispondente . Nell'esempio fornito, ad esempio, è possibile cercare l'utente nel database se è idstato fornito e assegnarlo a req.user.

Di seguito, potresti avere un percorso come:

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

Poiché / users / 123 corrisponderà prima al percorso nel tuo esempio, ciò controllerà e troverà prima l'utente 123; quindi /userspuò fare qualcosa con il risultato di quello.

Il middleware di route è uno strumento più flessibile e potente, sebbene, a mio avviso, poiché non si basa su un particolare schema URI o su un ordinamento di route. Sarei propenso a modellare l'esempio mostrato in questo modo, assumendo un Usersmodello con un asincrono 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);
});

Essere in grado di controllare il flusso in questo modo è abbastanza utile. È possibile che alcune pagine siano disponibili solo per gli utenti con un flag di amministrazione:

/**
 * 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');
});

Spero che questo ti abbia dato l'ispirazione!


Potresti dire che! Sarei più propenso a fare questo genere di cose con il middleware di route , poiché non associa la logica a un particolare ordine di route o a particolari strutture URI.
Asherah,

5
perché a volte torni successivo () ma a volte no
John

6
@John: il valore restituito viene effettivamente ignorato; Voglio solo tornare lì per assicurarmi di non chiamare next()più. Sarebbe lo stesso se avessi appena usato next(new Error(…)); return;.
Asherah,

1
@ livello0: il valore restituito viene ignorato; puoi considerarlo una scorciatoia per next(new Error(…)); return;. Se passiamo un valore a next, viene considerato unilateralmente un errore . Non ho esaminato troppo il codice espresso, ma scavo in giro e troverai quello che ti serve :)
Asherah

1
@ level0: (ho cambiato return next(…);per next(…); return;quindi è meno confusione.)
Asherah

87

Ho anche avuto problemi a capire il prossimo (), ma questo mi ha aiutato

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);

3
Molto succinta! Grazie! Ma come fai ad assicurarti che .getvenga chiamato il primo e non il secondo?
JohnnyQ,

18
@JohnnyQ Sarà un'esecuzione da cima a fondo
Tapash

59

Prima di capire next, devi avere una piccola idea del ciclo Richiesta-Risposta nel nodo, anche se non molto nei dettagli. Inizia con una richiesta HTTP per una particolare risorsa e termina quando rispedisci una risposta all'utente, ad esempio quando incontri qualcosa come res.send ('Hello World');

diamo un'occhiata a un esempio molto semplice.

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

Qui non è necessario next (), poiché resp.send termina il ciclo e restituisce il controllo al middleware di route.

Ora diamo un'occhiata a un altro esempio.

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

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

Qui abbiamo 2 funzioni middleware per lo stesso percorso. Ma otterrai sempre la risposta dal primo. Perché quello viene montato per primo nello stack del middleware e res.send terminerà il ciclo.

E se non volessimo sempre il "Hello World !!!!" risposta indietro. Per alcune condizioni potremmo volere il "Hello Planet !!!!" risposta. Modifichiamo il codice sopra e vediamo cosa succede.

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 !!!!");
});

Cosa ci nextfa qui. E sì, potresti avere dei problemi. Salterà la prima funzione middleware se la condizione è vera e invocherà la successiva funzione middleware e avrai la "Hello Planet !!!!"risposta.

Quindi, passa successivamente il controllo alla funzione successiva nello stack del middleware.

Che cosa succede se la prima funzione middleware non restituisce alcuna risposta ma esegue una parte logica e quindi si ottiene la risposta dalla seconda funzione middleware.

Qualcosa come sotto: -

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

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

In questo caso sono necessarie entrambe le funzioni del middleware per essere invocate. Quindi, l'unico modo per raggiungere la seconda funzione middleware è chiamare next ();

Che cosa succede se non si effettua una chiamata al prossimo. Non aspettatevi che la seconda funzione del middleware venga invocata automaticamente. Dopo aver richiamato la prima funzione, la tua richiesta verrà lasciata in sospeso. La seconda funzione non verrà mai invocata e non verrà restituita la risposta.


Quindi si next()comporta come un gotocon un'etichetta cablata? Cioè, nel tuo terzo frammento, una volta chiamato next(), res.send("Hello World !!!!"); non verrebbe mai eseguito? Ho notato che @Ashe ha sempre avuto una chiamata return;successiva con nextcodice nello stesso albero di esecuzione ... Immagino che potrei sempre fare il check in express, eh? / corre verso il suo editor di testo;)
ruffin

@Ruffin sì, puoi pensare al prossimo simile a un goto. ma il prossimo sa dove andare a differenza di goto che richiede un'etichetta. Successivamente passerà il controllo alla successiva funzione middleware. Inoltre, puoi nominare "prossimo" tutto ciò che ti piace. È solo un'etichetta qui. Ma la migliore pratica è usare il nome 'next'
Mav55

3
Ok, sembra che non sia accurato. Ho provato il codice ( qui pastebin ) e il codice dopo la next()chiamata viene chiamato . In questo caso, past the next() callviene scritto sulla console e quindi viene visualizzato un Error: Can't set headers after they are sent.errore, come res.sendviene chiamato il secondo , anche se senza successo. Il flusso di codice ritorna dopo la next()chiamata, il che rende importante @ Ashe returns(o altra gestione della logica).
ruffin,

4
@Ruffin, sì hai ragione. Abbiamo bisogno di una dichiarazione di ritorno dopo il next()per saltare l'esecuzione delle dichiarazioni rimanenti. grazie per averlo sottolineato.
Mav55

1
Grazie per aver effettivamente spiegato che cosa è il "middleware" con esempi chiari e non solo pappagallo della documentazione. Questa è stata l'unica risposta che in realtà dice qualcosa di chiaro su cosa succede, perché e come.
mc01


5

La chiamata a questa funzione richiama la funzione middleware successiva nell'app. La funzione next () non fa parte di Node.js o Express API, ma è il terzo argomento che viene passato alla funzione middleware. La funzione next () può essere nominata in qualsiasi modo, ma per convenzione viene sempre chiamata "next".


2

L'esecuzione della nextfunzione notifica al server che è stato completato con questo passaggio del middleware e può eseguire il passaggio successivo nella catena.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.