【问题标题】:Express next function, what is it really for?表达下一个函数,它到底是干什么用的?
【发布时间】:2012-10-19 10:37:50
【问题描述】:

一直在努力寻找对next() 方法作用的良好描述。在 Express 文档中,它说 next('route') 可用于跳转到该路由并跳过其间的所有路由,但有时会在不带参数的情况下调用 next。有人知道描述next 函数的好教程等吗?

【问题讨论】:

    标签: node.js express


    【解决方案1】:

    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() {
      //...
    });
    

    【讨论】:

    • 很好的答案!我也看到了 next 的一些其他用法,例如下一个(错误)和下一个('路线')。你现在这些的目的是什么,你想什么时候传播一个错误,什么时候你想跳转到某个路由?
    • @AndreasSelenwall next('route') 特定于app.VERB(),当路由有多个回调以“绕过剩余的路由回调。”next(err) 时使用用于跳转到任何“error middleware”(来自帖子的E)。
    • 赞成。就因为这句话:“开个玩笑,我真的不想处理这个”,你让我明白我在找什么。看到很多类似的问题,一言以蔽之。
    • 在没有参数的情况下调用next() 并不能真正告诉系统“我不想处理这个”,它只是告诉系统在这个完成后继续处理任何剩余的中间件。调用next() 不是错误情况,它是正常流程的一部分。如果您不致电next(),则根本不会处理其他路线。
    • 我希望这个网站上的更多答案是为外行写的......'next() 没有参数说“开玩笑,我真的不想处理这个”' - 非常适合新手...(@d512 有更好的解释)
    【解决方案2】:

    在大多数框架中,您都会收到一个请求并想要返回一个响应。由于 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!')
    })
    

    【讨论】:

    • 你会在哪里存储这个匿名日志功能?
    • "Express 将中止当前堆栈并运行所有具有 4 个参数的中间件。"我想知道 express 如何能够运行精确的错误处理程序中间件。谢谢!
    • @AbhishekAgarwal 也有同样的疑问,很好的综合。
    【解决方案3】:

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

    【讨论】:

    • 或者下一个中间件。
    【解决方案4】:

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

    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() 告诉它。

    【讨论】:

    • Sonfar 是我的最佳答案。
    • 好解释!
    • 我喜欢这个答案和你的描述。谢谢。
    【解决方案5】:

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

    1. next() 的用法:

    简而言之:下一个中间件函数。

    摘自本官方Express JS documentation - 'writing-middleware' page:

    “中间件函数 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)
    

    This page of Express JS documentation 声明“如果当前中间件函数没有结束请求-响应循环,它必须调用 next() 将控制权传递给下一个中间件函数。否则,请求将被挂起。”

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

    简而言之:next route(与 next 中间件函数在 next() 的情况下)

    从这个 Express JS documentation - 'using-middleware' page中提取:

    “要跳过路由器中间件堆栈中的其余中间件函数,请调用 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')
    })
    

    【讨论】:

    • next('route') 的很好解释。这是一个被低估的答案,它应该有更多的选票。
    【解决方案6】:

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

    干杯

    【讨论】:

      【解决方案7】:

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

      【讨论】:

        【解决方案8】:

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

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

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

        next() 当多个中间件函数传递给 app.use 或 app.METHOD 时,需要在每个中间件函数内调用 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();
        })
        

        【讨论】:

          【解决方案9】:

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

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2018-07-27
            • 2016-12-29
            • 1970-01-01
            • 2022-11-15
            • 1970-01-01
            • 2020-05-03
            • 1970-01-01
            • 2016-04-27
            相关资源
            最近更新 更多