【问题标题】:When to use next() and return next() in Node.js在 Node.js 中何时使用 next() 并返回 next()
【发布时间】:2013-05-24 12:14:58
【问题描述】:

场景:考虑以下是来自节点网络应用程序的代码部分。

app.get('/users/:id?', function(req, res, next){
    var id = req.params.id;
    if (id) {
        // do something
    } else {
        next(); //or return next();
    }
});

问题:我正在检查与next()return next() 一起使用的那个。上面的示例代码对两者的工作方式完全相同,并且在执行方面没有任何差异。

问题:有人可以说明一下,什么时候使用next(),什么时候使用return next(),以及一些重要的区别?

【问题讨论】:

    标签: node.js express connect v8


    【解决方案1】:

    正如@Laurent Perrin 的回答:

    如果你不这样做,你可能会在以后再次触发回调,这通常会带来毁灭性的后果

    如果你这样写中间件,我这里举个例子:

    app.use((req, res, next) => {
      console.log('This is a middleware')
      next()
      console.log('This is first-half middleware')
    })
    
    app.use((req, res, next) => {
      console.log('This is second middleware')
      next()
    })
    
    app.use((req, res, next) => {
      console.log('This is third middleware')
      next()
    })
    

    你会发现控制台的输出是:

    This is a middleware
    This is second middleware
    This is third middleware
    This is first-half middleware
    

    即在所有中间件函数完成后运行next()下面的代码。

    但是,如果你使用return next(),它会立即跳出回调,并且回调中return next()下面的代码将无法访问。

    【讨论】:

    • 作为express 的初学者,这个答案比其他答案让我更清楚。竖起大拇指!
    • 在这种情况下res.redirect('/')return res.redirect('/') 会不会有类似的情况?或许在res 语句前总是写return 会更好,以避免在发送后设置标题错误?
    • 为什么要在next()之后写代码?我在中间件中完成任务后什么都不做,这不是很明显吗? @PJCHENder
    • @ImranPollob 有时会发生错误。当您编写大量代码时,ifs/elses/etc.你可能会忘记```return next()`
    【解决方案2】:

    有些人总是写return next()是为了保证触发回调后执行停止。

    如果您不这样做,您可能会在以后再次触发回调,这通常会产生毁灭性的后果。您的代码照原样很好,但我会将其重写为:

    app.get('/users/:id?', function(req, res, next){
        var id = req.params.id;
    
        if(!id)
            return next();
    
        // do something
    });
    

    它为我节省了一个缩进级别,当我稍后再次阅读代码时,我确定next 不会被调用两次。

    【讨论】:

    • 在这种情况下,res.redirect('/')return res.redirect('/') 是否会发生类似的情况?也许最好总是在 res 语句之前写 return 以避免发送后设置 headers 错误?
    • @AdamD 我也想知道。
    • @theprogrammer 是的,我认为这个答案同样适用于res.redirect 之类的东西,最好使用它,除非您在重定向用户。
    【解决方案3】:

    next()connect middleware 的一部分。路由流的回调并不关心你是否从你的函数中返回任何东西,所以return next()next(); return; 基本上是一样的。

    如果您想停止功能流,可以使用next(err),如下所示

    app.get('/user/:id?', 
        function(req, res, next) { 
            console.log('function one');
            if ( !req.params.id ) 
                next('No ID'); // This will return error
            else   
                next(); // This will continue to function 2
        },
        function(req, res) { 
            console.log('function two'); 
        }
    );
    

    next() 几乎是用来扩展你请求的中间件的。

    【讨论】:

    • 我们可以发送参数如:next('No ID') 吗?
    • next('No ID') 实际上是在发送错误,这会中断流程。
    • 使用下一个(null, "somevalue");对于像 async.waterfall 这样的工具,它会将值传递给下一个函数。对于数据驱动的复杂交互系列,我通常在函数之间传递一个上下文对象。这样我就可以创建可以跨多个端点共享的通用函数,并通过上下文中的数据控制流
    • "所以 return next() 和 next(); return; 基本相同。" - 正是我需要阅读的内容。谢谢@dinchev
    • 我观察到相反的情况(触发错误时):next(error) 触发下一个中间件,但继续执行代码; return next(error) 只是将执行委托给下一个中间件。 next(e) 和 return next(e) 不一样。
    【解决方案4】:

    next() 和 return next() 的区别很简单,作为另一个编程原理。部分代码解释如下:

        app.use((req, res, next) => {
           console.log('Calling first middleware');
           next();
           console.log('Calling after the next() function');
        });
    
    
        app.use((req, res, next) => {
           console.log('Calling second middleware');
           return next(); // It returns the function block immediately and call next() function so the return next(); and next(); return; are the same
           console.log('After calling return next()');
        });
    
    

    输出是

    调用第一个中间件
    在 next() 函数之后调用
    调用第二个中间件

    【讨论】:

      【解决方案5】:
      import express from "express"
        
      const app = express()
      // API for the testing of next() 
      app.get(
        '/next', function (req,res,next) { 
          console.log('hi there ');
          next();
          console.log('you are still here');
        }
      )
        
      // API for the testing of return next() 
      app.get(
        '/return-next', function (req,res,next) { 
          console.log('hi there');
          return next(); 
          console.log('you are still here');
        }
      )
        
      app.listen(5000,()=> {
        console.log("App is running on port 5000")
      })
      

      next() in /**next** 路由将调用中间件,在中间件执行后,它会返回调用它的位置(与函数调用相同)并执行其余代码

      输出

      hi there
      
      you are still here
      

      /**return-next** 路由中,next() 前面有一个返回,它返回控制器

      输出

      hi there 
      

      如果您将next() 视为函数调用,您可以正确理解它

      【讨论】:

        【解决方案6】:

        最好不要使用它!我解释,这也是我所做的解释。

        next() 函数可以具有任何名称并且按照约定已设置为 next。它与通常在同一 URI 资源上执行的操作(PUT、GET、DELETE...)间接相关,例如 / user /: id

        app.get('/user/:id', function (req,res,next)...)
        app.put('/user/:id', function (req,res,next)...)
        app.delete('/user/:id', function (req,res,next)...)
        app.post('/user/', function ()...)
        

        现在,如果您查看 app.get、app.put 和 app.delete 使用相同的 uri (/user/:id),唯一不同的是它们的实现。当发出请求 (req) express 将 req 首先放在 app.get 中,如果您创建的任何验证因为该请求不是针对该控制器而失败,它会将 req 传递给 app.put,这是 te 文件中的下一个路由,所以在。如下例所示。

            app.get('/user/:id', function (req,res,next){
        
            if(req.method === 'GET')
            //whatever you are going to do
            else
              return next() //it passes the request to app.put
        
            //Where would GET response 404 go, here? or in the next one. 
            // Will the GET answer be handled by a PUT? Something is wrong here.
        
           })
            app.put('/user/:id', function (req,res,next){
        
            if(req.method === 'PUT')
            //whatever you are going to do
            else
              return next()
        
           })
        

        问题在于,最终你最终将 req 传递给所有控制器,希望通过 req 的验证有一个控制器可以满足你的要求。最后,所有控制器最终都会收到不适合他们的东西:(。

        那么,如何避免next()的问题

        答案很简单。

        1-应该只有一个 uri 来识别资源

        http://IpServidor/colection/:resource/colection/:resource 如果你的 URI 比这个长,你应该考虑创建一个新的 uri

        例如http://IpServidor/users/pepe/contacts/contacto1

        2-对该资源的所有操作都必须尊重动词http(get、post、put、delete……)的幂等性,因此对URI的调用实际上只有一种调用方式

        POST http://IpServidor/users/  //create a pepe user 
        GET http://IpServidor/users/pepe  //user pepe returns   
        PUT http://IpServidor/users/pepe  //update the user pepe 
        DELETE http://IpServidor/users/pepe  //remove the user pepe
        

        更多信息 [https://docs.microsoft.com/es-es/azure/architecture/best-practices/api-design#organize-the-api-around-resources][1]

        我们看代码!让我们避免使用next()的具体实现!

        在 index.js 文件中

        //index.js the entry point to the application also caller app.js
        const express = require('express');
        const app = express();
        
        const usersRoute = require('./src/route/usersRoute.js');
        
        app.use('/users', usersRoute );
        

        在文件 usersRoute.js 中

            //usersRoute.js
            const express = require('express');
            const router = express.Router();
        
            const getUsersController = require('../Controllers/getUsersController.js');
            const deleteUsersController = require('../Controllers/deleteUsersController.js');
        
            router.use('/:name', function (req, res) //The path is in /users/:name
            {
            switch (req.method)
            {
            case 'DELETE':
              deleteUsersController(req, res);
              break;
            case 'PUT':
             // call to putUsersController(req, res);
             break;
            case 'GET':
             getUsersController(req, res);
             break;
            default:
             res.status(400).send('Bad request');
            } });
        
        router.post('/',function (req,res) //The path is in /users/
        {
            postUsersController(req, res);
        });
        
        module.exports = router;
        

        现在 usersRoute.js 文件完成了一个名为 usersRoute 的文件的预期工作,即管理 URI /users/ 的路由

        //文件getUsersController.js

        //getUsersController.js
            const findUser= require('../Aplication/findUser.js');
            const usersRepository = require('../Infraestructure/usersRepository.js');
        
            const getUsersController = async function (req, res)
            {
        
               try{
                  const userName = req.params.name;
                //...
                  res.status(200).send(user.propertys())
        
                }catch(findUserError){
                   res.status(findUserError.code).send(findUserError.message)
                }
            }
           module.exports = getUsersController;
        

        通过这种方式,您可以避免使用 next,解耦代码,提高性能,开发 SOLID,为可能的微服务迁移敞开大门,最重要的是,程序员易于阅读.

        【讨论】:

        • 这是不正确的,app.get 不会像你建议的那样传递给 app.put。只有匹配的请求才会被调用,所以如果方法是 GET,只会调用 app.get 中间件。中间件不需要检查请求方法。您的建议忽略了 express 的主要功能,而是实现了您自己的路由。此外,您的建议假设您的路由是您将使用的唯一中间件,因为它永远不会在任何地方传递。
        猜你喜欢
        • 2015-01-17
        • 1970-01-01
        • 1970-01-01
        • 2011-08-09
        • 2021-08-05
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多