À quoi sert le paramètre «suivant» dans Express?


295

Supposons que vous ayez un simple bloc de code comme celui-ci:

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

Cette fonction a deux paramètres, reqet res, qui représentent respectivement les objets de demande et de réponse.

D'autre part, il existe d'autres fonctions avec un troisième paramètre appelé next. Par exemple, jetons un œil au code suivant:

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

Je ne comprends pas à quoi ça next()sert ni pourquoi il est utilisé. Dans cet exemple, si id n'existe pas, que fait-il nextréellement?


13
Next permet simplement au prochain gestionnaire d'itinéraire en ligne de traiter la demande. Dans ce cas, si l'ID utilisateur existe, il sera probablement utilisé res.sendpour terminer la demande. S'il n'existe pas, il existe probablement un autre gestionnaire qui émettra une erreur et terminera la demande.
Dominic Barnes

1
vous dites donc que j'ai un app.post('/login',function(req,res))après app.get('/users',function(req,res)) qu'il appellera la connexion comme la prochaine route dans le fichier app.js en appelant next ()?
Menztrual

2
Non, vous devez vous référer à cette partie de la documentation Express.js: expressjs.com/guide.html#passing-route control
Dominic Barnes

3
Fondamentalement, la prochaine route à exécuter sera une autre que l'URL de la demande correspond. Dans ce cas, si un autre itinéraire a été enregistré via app.get("/users"), il sera exécuté si le gestionnaire ci-dessus appelle ensuite.
Dominic Barnes

3
Le suivant est simplement un rappel.
Jonathan Ong

Réponses:


266

Il passe le contrôle à la prochaine route correspondante . Dans l'exemple que vous donnez, par exemple, vous pouvez rechercher l'utilisateur dans la base de données si un a idété donné et l'affecter àreq.user .

Ci-dessous, vous pourriez avoir un itinéraire comme:

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

Puisque / users / 123 correspondra d'abord à l'itinéraire dans votre exemple, cela vérifiera et trouvera d'abord l'utilisateur 123; /userspeut alors faire quelque chose avec le résultat de cela.

Le middleware de route est un outil plus flexible et plus puissant, à mon avis, car il ne dépend pas d'un schéma d'URI ou d'un ordre de routage particulier. Je serais enclin à modéliser l'exemple montré comme ceci, en supposant un Usersmodèle avec une async 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);
});

Être capable de contrôler le flux comme celui-ci est assez pratique. Vous souhaiterez peut-être que certaines pages ne soient accessibles qu'aux utilisateurs disposant d'un indicateur d'administrateur:

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

J'espère que cela vous a donné de l'inspiration!


Tu pourrais dire ça! Je serais plus enclin à faire ce genre de chose avec le middleware de route , car cela ne couple pas la logique à un ordre particulier de routes ou à des structures d'URI particulières.
Asherah

5
pourquoi parfois vous revenez ensuite () mais parfois non
John

6
@John: la valeur de retour est en fait ignorée; Je veux juste y retourner pour m'assurer de ne pas rappeler next(). Ce serait la même chose si je viens de l'utiliser next(new Error(…)); return;.
Asherah

1
@ level0: la valeur de retour est ignorée; vous pouvez le considérer comme un raccourci next(new Error(…)); return;. Si nous transmettons une valeur à next, elle est considérée unilatéralement comme une erreur . Je n'ai pas trop étudié le code express, mais fouillez et vous trouverez ce dont vous avez besoin :)
Asherah

1
@ level0: (J'ai changé return next(…);pour next(…); return;donc c'est moins déroutant.)
Asherah

87

J'ai également eu du mal à comprendre ensuite (), mais cela a aidé

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
Très succinct! Merci! Mais comment vous assurer que le premier .getest appelé et non le 2e?
JohnnyQ

18
@JohnnyQ Ce sera une exécution de haut en bas
Tapash

59

Avant de comprendre next, vous devez avoir une petite idée du cycle demande-réponse dans le nœud, mais pas beaucoup en détail. Cela commence lorsque vous faites une requête HTTP pour une ressource particulière et se termine lorsque vous renvoyez une réponse à l'utilisateur, c'est-à-dire lorsque vous rencontrez quelque chose comme res.send ('Hello World');

regardons un exemple très simple.

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

Ici, nous n'avons pas besoin de next (), car resp.send mettra fin au cycle et remettra le contrôle au middleware de route.

Voyons maintenant un autre exemple.

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

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

Ici, nous avons 2 fonctions middleware pour le même chemin. Mais vous obtiendrez toujours la réponse du premier. Parce qu'il est monté en premier dans la pile du middleware et que res.send mettra fin au cycle.

Mais que se passe-t-il si nous ne voulons pas toujours le "Hello World !!!!" réponse en retour. Pour certaines conditions, nous pouvons vouloir le "Hello Planet !!!!" réponse. Modifions le code ci-dessus et voyons ce qui se passe.

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

Que nextfait-on ici. Et oui, vous pourriez avoir des bus. Cela va sauter la première fonction middleware si la condition est vraie et appeler la prochaine fonction middleware et vous aurez la "Hello Planet !!!!"réponse.

Donc, passez ensuite le contrôle à la fonction suivante dans la pile du middleware.

Que se passe-t-il si la première fonction middleware ne renvoie aucune réponse mais exécute un morceau de logique et que vous obtenez ensuite la réponse de la deuxième fonction middleware.

Quelque chose comme ci-dessous: -

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

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

Dans ce cas, vous devez appeler les deux fonctions middleware. Ainsi, la seule façon d'atteindre la deuxième fonction middleware est d'appeler next ();

Et si vous n'appelez pas au suivant. Ne vous attendez pas à ce que la deuxième fonction middleware soit invoquée automatiquement. Après avoir invoqué la première fonction, votre demande restera suspendue. La deuxième fonction ne sera jamais invoquée et vous ne récupérerez pas la réponse.


Se next()comporte donc comme un gotoavec une étiquette câblée? Autrement dit, dans votre troisième extrait, une fois que vous appelez next(), res.send("Hello World !!!!"); ne serait jamais exécuté? J'ai remarqué que @Ashe avait toujours un return;après- nextappel qui avait du code dans la même arborescence d'exécution ... Je suppose que je pouvais toujours m'enregistrer express, hein? / court vers son éditeur de texte;)
ruffin

@ruffin oui, vous pouvez penser à un semblable à un goto. mais sait ensuite où aller contrairement à goto qui nécessite une étiquette. Next passera le contrôle à la prochaine fonction middleware. Vous pouvez également nommer «suivant» tout ce que vous voulez. C'est juste une étiquette ici. Mais la meilleure pratique consiste à utiliser le nom «suivant»
Mav55

3
D'accord, on dirait que ce n'est pas exact. J'ai essayé le code ( pastebin ici ) et le code après l' next()appel est appelé . Dans ce cas, past the next() callest écrit sur la console, puis j'obtiens une Error: Can't set headers after they are sent.erreur, car la seconde res.sendest appelée, mais sans succès. Le flux de code revient après l' next()appel, ce qui rend important @ Ashe returns(ou toute autre gestion logique).
ruffin

4
@ruffin, ouais tu as raison. Nous avons besoin d'une instruction de retour après le next()pour ignorer l'exécution des instructions restantes. merci de l'avoir signalé.
Mav55

1
Merci d'avoir réellement expliqué ce qu'est un «middleware» avec des exemples clairs et pas seulement en perroquetant la documentation. C'était la seule réponse qui dit en fait quelque chose de clair sur ce qui se passe, pourquoi et comment.
mc01


5

L'appel de cette fonction appelle la prochaine fonction middleware de l'application. La fonction next () ne fait pas partie de Node.js ou de l'API Express, mais est le troisième argument transmis à la fonction middleware. La fonction next () peut être nommée n'importe quoi, mais par convention, elle est toujours nommée «next».


2

L'exécution de la nextfonction informe le serveur que vous avez terminé cette étape de middleware et peut exécuter l'étape suivante de la chaîne.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.