169

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

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()以及一些重要的区别?

4

6 回答 6

275

正如@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()将无法访问。

于 2017-04-14T02:01:22.470 回答
183

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

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

app.get('/users/:id?', function(req, res, next){
    var id = req.params.id;

    if(!id)
        return next();

    // do something
});

它为我节省了一个缩进级别,当我稍后再次阅读代码时,我确信没有办法next被调用两次。

于 2013-05-29T10:53:14.233 回答
50

next()连接中间件的一部分。路由器流的回调并不关心您是否从函数中返回任何内容,因此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()用于扩展您的请求的中间件。

于 2013-05-29T11:01:29.620 回答
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() 函数之后
调用 调用第二个中间件

于 2021-02-08T20:56:38.960 回答
1
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**route 将调用中间件,在中间件执行后,它会返回到调用它的位置(与函数调用相同)并执行其余代码

输出

hi there

you are still here

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

输出

hi there 

如果您考虑next()像函数调用一样,您可以正确理解它

于 2021-08-23T06:35:31.643 回答
0

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

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,为可能迁移到微服务敞开大门,最重要的是,它易于程序员阅读。

于 2019-12-17T11:57:03.737 回答