362

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

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实际上在做什么?

4

5 回答 5

298

它将控制权传递给下一个匹配的路由。例如,在您给出的示例中,如果给出了 an,您可能会在数据库中查找用户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);
});

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

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

希望这能给你一些启发!

于 2012-05-22T04:26:39.667 回答
113

我也有理解 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);
于 2016-06-25T15:59:33.133 回答
93

在理解之前next,你需要对 node 中的 Request-Response 循环有一点了解,虽然不是很详细。它从您对特定资源发出 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();

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

于 2018-03-20T15:17:16.320 回答
15

Next 用于将控制权传递给下一个中间件函数。如果不是,该请求将被挂起或打开。

于 2017-04-14T19:58:10.713 回答
0

我还想补充一下为什么express不调用下一个中间件并让我们控制它。由于 node 是异步的,如果 express 调用下一个中间件而不等待某个异步调用完成,则响应可能不完整或包含错误,因此用户可以控制何时调用下一个中间件函数。

于 2022-01-01T11:40:15.913 回答