152

一直试图找到一个很好的描述该next()方法的作用。在 Express 文档中,它说next('route')可以用来跳转到该路由并跳过其间的所有路由,但有时next在没有参数的情况下调用。有人知道描述该next功能的好教程等吗?

4

9 回答 9

193

next()没有争论说“开个玩笑,我真的不想处理这个”。它返回并尝试找到下一条匹配的路线。

这很有用,比如说如果你想拥有某种带有 url slug 的页面管理器,以及许多其他的东西,但这里有一个例子。

app.get('/:pageslug', function(req, res, next){
  var page = db.findPage(req.params.pageslug);
  if (page) {
    res.send(page.body);
  } else {
    next();
  }
});

app.get('/other_routes', function() {
  //...
});

组成的代码应该检查数据库以查找具有特定 id slug 的页面。如果它找到一个渲染它!如果找不到,则忽略此路由处理程序并检查其他路由处理程序。

因此next(),没有参数允许假装您没有处理该路线,以便其他东西可以代替它。


或带有 的命中计数器app.all('*')。它允许您执行一些共享设置代码,然后转到其他路线以执行更具体的操作。

app.all('*', function(req, res, next){
  myHitCounter.count += 1;
  next();
});

app.get('/other_routes', function() {
  //...
});
于 2012-10-30T05:26:37.467 回答
143

在大多数框架中,您都会收到一个请求并且想要返回一个响应。由于 Node.js 的异步特性,如果你在做一些不平凡的事情,你会遇到嵌套回调的问题。为了防止这种情况发生,Connect.js(在 v4.0 之前,Express.js 是 connect.js 之上的一层)有一个叫做中间件的东西,它是一个带有 2、3 或 4 个参数的函数。

function (<err>, req, res, next) {}

您的 Express.js 应用程序是这些功能的堆栈。

在此处输入图像描述

路由器很特别,它是一个中间件,可以让你为某个 url 执行一个或多个中间件。所以它是堆栈中的堆栈。

那么下一步做什么呢?很简单,它告诉您的应用程序运行下一个中间件。但是当你将某些东西传递给下一个时会发生什么?Express 将中止当前堆栈并运行所有具有 4 个参数的中间件。

function (err, req, res, next) {}

该中间件用于处理任何错误。我喜欢做以下事情:

next({ type: 'database', error: 'datacenter blew up' });

有了这个错误,我可能会告诉用户出了点问题并记录真正的错误。

function (err, req, res, next) {
   if (err.type === 'database') {
     res.send('Something went wrong user');
     console.log(err.error);
   }
};

如果你把你的 Express.js 应用程序想象成一个堆栈,你可能会自己解决很多奇怪的问题。例如,当您在路由器之后添加 Cookie 中间件时,您的路由不会有 cookie 是有道理的。

文档

您定义错误处理中间件的方式与其他中间件相同,不同之处在于使用四个参数而不是三个;特别是签名(err,req,res,next):

app.use(function (err, req, res, next) {
  console.error(err.stack)
  res.status(500).send('Something broke!')
})
于 2012-10-30T18:41:16.157 回答
112

恕我直言,这个问题的公认答案并不准确。正如其他人所说,它实际上是关于控制链中的下一个处理程序何时运行。但我想提供更多代码以使其更具体。假设您有这个简单的快递应用程序:

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

app.get('/user/:id', function (req, res, next) {
    console.log('before request handler');
    next();
});

app.get('/user/:id', function (req, res, next) {
    console.log('handling request');
    res.sendStatus(200);
    next();
});

app.get('/user/:id', function (req, res, next) {
    console.log('after request handler');
    next();
});

app.listen(3000, function () {
    console.log('Example app listening on port 3000!')
});

如果你这样做

curl http://localhost:3000/user/123

你会看到这个打印到控制台:

before request handler
handling request
after request handler

现在,如果您像这样注释掉next()中间处理程序中的调用:

app.get('/user/:id', function (req, res, next) {
    console.log('handling request');
    res.sendStatus(200);
    //next();
});

您将在控制台上看到:

before request handler
handling request

请注意,最后一个处理程序(打印的那个after request handler)没有运行。那是因为您不再告诉 express 运行下一个处理程序。

因此,您的“主”处理程序(返回 200 的处理程序)是否成功并不重要,如果您希望其余中间件运行,则必须调用next().

这什么时候会派上用场?假设您要记录进入某个数据库的所有请求,无论请求是否成功。

app.get('/user/:id', function (req, res, next) {
    try {
       // ...
    }
    catch (ex) {
       // ...
    }
    finally {
       // go to the next handler regardless of what happened in this one
       next();
    }
});

app.get('/user/:id', function (req, res, next) {
    logToDatabase(req);
    next();
});

如果要运行第二个处理程序,则必须调用next()第一个处理程序。

请记住,节点是异步的,因此它无法知道第一个处理程序的回调何时完成。你必须打电话告诉它next()

于 2017-09-08T18:12:16.660 回答
11

不带参数的 next() 调用框架中的下一个路由处理程序或下一个中间件。

于 2015-06-24T20:23:43.377 回答
5

在一处总结正确提到的答案,

  • next() : 将控制移动到同一路径中的下一个函数。单一路线中的​​多个功能的情况。
  • next('route') :通过跳过当前路线中的所有剩余功能将控制移动到下一条路线。
  • next(err) : 将控制转移到错误中间件
app.get('/testroute/:id', function (req, res, next) {
  if (req.params.id === '0') next() // Take me to the next function in current route
  else if (req.params.id === '1') next('route') //Take me to next routes/middleware by skipping all other functions in current router  
  else next(new Error('Take me directly to error handler middleware by skipping all other routers/middlewares'))
}, function (req, res, next) {
  // render a regular page
  console.log('Next function in current route')
  res.status(200).send('Next function in current route');
})

// handler for the /testroute/:id path, which renders a special page
app.get('/testroute/:id', function (req, res, next) {
  console.log('Next routes/middleware by skipping all other functions in current router')
  res.status(200).send('Next routes/middleware by skipping all other functions in current router');
}) 
//error middleware
app.use(function (err, req, res, next) {
  console.log('take me to next routes/middleware by skipping all other functions in current router')
  res.status(err.status || 500).send(err.message);
});

于 2021-01-25T01:04:30.303 回答
3

问题还询问了关于 next('route') 的使用,到目前为止,它似乎在一周内提供的答案中涵盖:

  1. next() 的用法:

简而言之:下一个中间件功能。

摘自此官方Express JS 文档 - “writing-middleware”页面

“中间件函数 myLogger 只是打印一条消息,然后通过调用 next() 函数将请求传递给堆栈中的下一个中间件函数。”

var express = require('express')
var app = express()

var myLogger = function (req, res, next) {
  console.log('LOGGED')
  next()
}

app.use(myLogger)

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

app.listen(3000)

Express JS 文档的这一页指出“如果当前中间件函数没有结束请求-响应周期,则必须调用 next() 将控制权传递给下一个中间件函数。否则,请求将被挂起。”

  1. next('route') 的用法:

简而言之:下一条路线(与next() 的情况下的下一个中间件函数

从此Express JS 文档中摘录 - 'using-middleware' 页面

“要从路由器中间件堆栈中跳过其余中间件函数,请调用 next('route') 将控制权传递给下一个路由。注意:next('route') 仅适用于通过使用加载的中间件函数app.METHOD() 或 router.METHOD() 函数。

此示例显示了一个中间件子堆栈,用于处理对 /user/:id 路径的 GET 请求。”

app.get('/user/:id', function (req, res, next) {
  // if the user ID is 0, skip to the next route
  if (req.params.id === '0') next('route')
  // otherwise pass the control to the next middleware function in this stack
  else next()
}, function (req, res, next) {
  // render a regular page
  res.render('regular')
})

// handler for the /user/:id path, which renders a special page
app.get('/user/:id', function (req, res, next) {
  res.render('special')
})
于 2018-06-21T14:21:10.567 回答
2

它只是意味着将控制权传递给下一个处理程序。

干杯

于 2019-01-02T19:53:26.030 回答
1

注意上面对 next() 的调用。调用此函数会调用应用程序中的下一个中间件函数。next() 函数不是 Node.js 或 Express API 的一部分,而是传递给中间件函数的第三个参数。next() 函数可以命名为任何名称,但按照惯例,它始终命名为“next”。为避免混淆,请始终使用此约定。

于 2019-12-07T05:53:27.547 回答
0

next()是带有 req 的中间件函数的回调参数,而 res 是下面代码中 next 的 http 请求和响应参数。

app.get('/', (req, res, next) => { next() });

所以 next() 调用传入的中间件函数。如果当前中间件函数没有结束请求-响应循环,它应该调用 next(),否则请求将被挂起并超时。

当向app.use或 app.METHOD传递多个中间件函数时,需要在每个中间件函数内调用next() fn ,否则将不会调用下一个中间件函数(如果传递了超过 1 个中间件函数)。要跳过调用剩余的中间件函数,请在中间件函数内调用next('route'),之后不应调用其他中间件函数。在下面的代码中,fn1 将被调用,fn2 也将被调用,因为 next() 在 fn1 中被调用。但是,不会调用 fn3,因为在 fn2 中调用了 next('route')。

app.get('/fetch', function fn1(req, res, next)  {
console.log("First middleware function called"); 
    next();
}, 
function fn2(req, res, next) {
    console.log("Second middleware function called"); 
    next("route");
}, 
function fn3(req, res, next) {
    console.log("Third middleware function will not be called"); 
    next();
})
于 2020-06-17T00:23:34.410 回答