何时在Node.js中使用next()并返回next()


136

场景:考虑以下是节点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()以及一些重要的区别吗?

Answers:


141

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

如果您不这样做,则可能会在第二次触发回调后冒着风险,这通常会造成灾难性的后果。您的代码可以正常使用,但我会将其重写为:

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

    if(!id)
        return next();

    // do something
});

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


2
在这种情况下,res.redirect('/')vs return res.redirect('/')。也会有类似的情况吗?也许最好总是在res语句前编写return,以避免在发送标头后设置标头的错误?
亚当D

185

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


29
作为一个初学者,express这个问题比其他答案更清楚。竖起大拇指!
普通话

1
在这种情况下,res.redirect('/')vs return res.redirect('/')。也会有类似的情况吗?也许最好总是returnres语句前写,以免在发送标头后设置标头出错?
亚当D

1
为什么要在next()之后编写代码?在中间件中完成任务后,我什么都不做很明显吗?@PJCHENder
Imran Pollob

1
@ImranPollob有时会发生错误。当您编写大量代码时,ifs / elses / etc。您可能会忘记```return next()`
Jone Polvora

46

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()用于扩展您的请求的中间件。


1
我们可以发送如下参数next('No ID')吗?
Amol M Kulkarni,

7
next('No ID')实际上正在发送错误,这会中断流程。
drinchev 2013年

使用next(null,“ somevalue”); 对于async.waterfall之类的工具,它将把值传递给下一个函数。对于数据驱动的一系列复杂交互,我通常在函数之间传递上下文对象。这样,我可以创建可以在多个端点之间共享的通用函数,并通过上下文中的数据控制流
Chad Wilson

5
“所以return next()和next(); return;基本相同。” -正是我需要阅读的内容。thx @drinchev
Nick Pineda

1
我观察到相反的情况(触发错误时):next(error)触发下一个中间件,但继续执行代码;return next(error)只是将执行降级到下一个中​​间件。next(e)和return next(e)不同。
Nickolodeon

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;

这样,您就可以避免使用下一个代码,解耦代码,提高性能,开发SOLID,为可能迁移到微服务打开大门,并且最重要的是,程序员易于阅读。


2
这是不正确的,app.get不会像您建议的那样传递给app.put。仅会调用匹配的请求,因此,如果方法是GET,则只会调用app.get中间件。中间件不需要检查请求方法。您的建议忽略了express的主要功能,而是实现了自己的路由。此外,您的建议假定路由是您将要使用的唯一中间件,因为它永远不会传递到任何地方。
Ravenex

信息不正确,请参见以上答案。
DD钻石

-3

下一个() :

调用此函数将调用应用程序中的下一个中间件函数。next()函数不是Node.js或Express API的一部分,而是传递给中间件函数的第三个参数。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.