Express中使用的“ next”参数是什么?


295

假设您有一个简单的代码块,如下所示:

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

此函数有两个参数reqres,分别代表请求和响应对象。

另一方面,其他函数的第三个参数称为next。例如,让我们看下面的代码:

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

我不明白这next()是什么意思或为什么要使用它。在该示例中,如果id不存在,那么next实际上在做什么?


13
Next仅允许排队的下一个路由处理程序处理请求。在这种情况下,如果存在用户标识,则它可能会用于res.send完成请求。如果不存在,则可能有另一个处理程序将发出错误并完成请求。
Dominic Barnes

1
所以你说我有一个app.post('/login',function(req,res))之后app.get('/users',function(req,res)) ,它将通过调用next()来将登录名作为app.js文件中的下一条路由?
Menztrual

2
不,您应该参考Express.js文档的这一部分:expressjs.com/guide.html#passing-route控制
Dominic Barnes,

3
基本上,下一条要运行的路由将是与该请求的URL匹配的另一条路由。在这种情况下,如果通过注册了另一个路由app.get("/users"),则如果上述处理程序接下来调用,则它将运行。
Dominic Barnes

3
下一步基本上只是一个回调。
Jonathan Ong

Answers:


266

它将控制权传递到下一个匹配的路由。例如,在给出的示例中,您可能会在数据库中查找用户(如果id给出了),然后将其分配给req.user

在下面,您可能会有一条类似的路线:

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

由于/ users / 123将首先匹配示例中的路由,因此将首先检查并找到user 123;然后/users可以这样做的结果。

我认为,路由中间件是一种更灵活,功能更强大的工具,因为它不依赖于特定的URI方案或路由排序。我倾向于对这样显示的示例进行建模,假设Users模型具有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);
});

能够像这样控制流量非常方便。您可能希望某些页面仅对带有admin标志的用户可用:

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

希望这给您一些启发!


你可以那样说!不过,我更倾向于使用路由中间件来做这种事情,因为它不会将逻辑耦合到特定的路由顺序或特定的URI结构。
亚谢拉2012年

5
为什么有时您返回next()但有时却不
John

6
@John:实际上忽略了返回值;我只是想回到那里以确保不再打给我next()。如果我刚用的话会一样的next(new Error(…)); return;
Asherah 2015年

1
@ level0:返回值被忽略;您可以将其视为简写next(new Error(…)); return;。如果将值传递给next则单方面将其视为错误。我没有研究过多的快速代码,但深入研究,您会发现您需要的东西:)
Asherah

1
@ level0 :(我已更改return next(…);next(…); return;它,因此不太混乱。)
Asherah,2016年

87

我也有问题理解的next(),但这种帮助

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
非常简洁!谢谢!但是如何确定.get第一个而不是第二个被调用?
JohnnyQ '17

18
@JohnnyQ这将是自上而下的执行
Tapash

59

在理解之前next,您需要稍微了解一下节点中的请求-响应周期。它从您对特定资源发出HTTP请求开始,并在您将响应发送回用户时结束,即遇到诸如res.send('Hello World')之类的内容时结束。

让我们看一个非常简单的例子。

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

这里我们不需要next(),因为resp.send将结束循环并将控制权交还给路由中间件。

现在让我们看另一个例子。

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

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

在这里,我们有2个中间件功能用于同一路径。但是您总是会得到第一个响应。因为首先安装在中间件堆栈中,然后res.send将结束循环。

但是,如果我们始终不希望出现“ Hello World !!!!”,该怎么办?回应回来。在某些情况下,我们可能需要“ Hello Planet !!!!” 响应。让我们修改上面的代码,看看会发生什么。

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

这是在next做什么 是的,你可能有脾气。如果条件为真,它将跳过第一个中间件函数,并调用下一个中间件函数,您将获得"Hello Planet !!!!"响应。

因此,接下来将控件传递给中间件堆栈中的下一个功能。

如果第一个中间件功能没有发回任何响应但执行了一段逻辑,然后又从第二个中间件功能得到了响应,该怎么办呢?

如下所示:

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

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

在这种情况下,您需要调用两个中间件功能。因此,达到第二个中间件功能的唯一方法是调用next();。

如果您不拨打电话,该怎么办。不要期望第二个中间件功能会自动被调用。调用第一个功能后,您的请求将被挂起。第二个函数将永远不会被调用,您也不会取回响应。


这样的next()表现就像goto带有硬接线标签的一样吗?也就是说,在您的第三个代码段中,一旦调用next()res.send("Hello World !!!!"); 将永远不会执行?我注意到@Ashe总是有一个return;after next调用,它们的代码在同一执行树中。/运行到他的文本编辑器中;)
鲁芬,

@ruffin是的,您可以想到类似于goto的内容。但是next知道去哪里,不像goto需要标签。Next会将控件传递给下一个中间件功能。另外,您可以根据自己的喜好命名“下一个”。这只是这里的标签。但是,最好的做法是使用名为“未来”
Mav55

3
好吧,看来这是不正确的。我试过代码(引擎收录在这里),并在之后的代码next()调用。在这种情况下,past the next() call将其写入控制台,然后出现Error: Can't set headers after they are sent.错误res.send,尽管调用失败,但第二个被调用。调用后代码流确实返回next(),这使得@Ashe returns(或其他逻辑管理)很重要。
鲁芬

4
@ruffin,是的,你是对的。我们需要在之后的return语句next()来跳过其余语句的执行。感谢您指出这一点。
Mav55

1
感谢您实际解释“中间件”是什么/是否有清楚的示例,而不仅仅是模仿文档。这是唯一能真正说明发生的事情,原因和方式的答案。
mc01


5

调用此函数将调用应用程序中的下一个中间件函数。next()函数不是Node.js或Express API的一部分,而是传递给中间件函数的第三个参数。next()函数可以命名为任何名称,但按照惯例,它始终命名为“ next”。


2

执行该next功能会通知服务器您已完成此中间件步骤,并且可以执行链中的下一步。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.