NodeJS 在Express中,参数“next”的作用是什么?

holgip5t  于 2022-11-03  发布在  Node.js
关注(0)|答案(6)|浏览(368)

假设您有一个简单的代码块,如下所示:

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

此函数有两个参数reqres,分别表示请求和响应对象。
另一方面,还有一些函数带有第三个参数next。例如,让我们看一下下面的代码:

app.get('/users/:id?', function(req, res, next){ // Why do we need next?
    var id = req.params.id;
    if (id) {
        // do something
    } else {
        next(); // What is this doing?
    }
});

我不明白next()的意义是什么,也不明白为什么要使用它。在那个例子中,如果id不存在,那么next实际上在做什么?

hujrc8aj

hujrc8aj1#

它将控制权传递给下一个匹配的路由。例如,在你给予的例子中,如果给定了id,你可以在数据库中查找用户,并将其分配给req.user
在下面,您可以使用如下路线:

app.get('/users', function(req, res) {
  // check for and maybe do something with req.user
});

由于/users/123将首先匹配示例中的路由,因此它将首先检查并找到用户123;那么/users可以对这个结果做些什么。
但是,在我看来,Route middleware是一个更灵活、更强大的工具,因为它不依赖于特定的URI方案或路由顺序。

function loadUser(req, res, next) {
  if (req.params.userId) {
    Users.findOne({ id: req.params.userId }, function(err, user) {
      if (err) {
        next(new Error("Couldn't find user: " + err));
        return;
      }

      req.user = user;
      next();
    });
  } else {
    next();
  }
}

// ...

app.get('/user/:userId', loadUser, function(req, res) {
  // do something with req.user
});

app.get('/users/:userId?', loadUser, function(req, res) {
  // if req.user was set, it's because userId was specified (and we found the user).
});

// Pretend there's a "loadItem()" which operates similarly, but with itemId.
app.get('/item/:itemId/addTo/:userId', loadItem, loadUser, function(req, res) {
  req.user.items.append(req.item.name);
});

能够像这样控制流程是非常方便的。您可能希望某些页面只对带有管理员标志的用户可用:

/**
 * Only allows the page to be accessed if the user is an admin.
 * Requires use of `loadUser` middleware.
 */
function requireAdmin(req, res, next) {
  if (!req.user || !req.user.admin) {
    next(new Error("Permission denied."));
    return;
  }

  next();
}

app.get('/top/secret', loadUser, requireAdmin, function(req, res) {
  res.send('blahblahblah');
});

希望这给了你一些启发!

68bkxrlz

68bkxrlz2#

我在理解next()时也遇到了问题,但this帮助了我

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

app.get("/", function(httpRequest, httpResponse, next){
    httpResponse.write("Hello");
    next(); //remove this and see what happens 
});

app.get("/", function(httpRequest, httpResponse, next){
    httpResponse.write(" World !!!");
    httpResponse.end();
});

app.listen(8080);
pprl5pva

pprl5pva3#

在理解 * next * 之前,你需要对节点中的请求-响应循环有一点了解,尽管没有太多细节。它从你对一个特定资源发出HTTP请求开始,当你向用户发送一个响应时结束,也就是当你遇到res.send('Hello World');
让我们看一个非常简单示例。

app.get('/hello', function (req, res, next) {
  res.send('USER')
})

这里我们不需要next(),因为resp.send将结束循环并将控制权交回给路由中间件。
现在让我们看另一个例子。

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

app.get('/hello', function (req, res, next) {
  res.send("Hello Planet !!!!");
});

在这里,我们有两个中间件函数用于相同的路径。但是你总是会从第一个函数得到响应。因为它是第一个安装在中间件堆栈中的,res.send将结束循环。
但是,如果我们总是不希望返回“Hello World!!!!”响应,在某些情况下,我们可能希望返回“Hello Planet!!!!”响应,让我们修改上面的代码,看看会发生什么。

app.get('/hello', function (req, res, next) {
  if(some condition){
    next();
    return;
  }
  res.send("Hello World !!!!");  
});

app.get('/hello', function (req, res, next) {
  res.send("Hello Planet !!!!");
});

next在这里做什么呢?是的,你可能会有猜测,如果条件为真,它会跳过第一个中间件函数,调用下一个中间件函数,你会得到"Hello Planet !!!!"的响应。
因此,接下来将控制传递给中间件堆栈中的下一个函数。
如果第一个中间件功能没有发送回任何响应,但执行了一段逻辑,然后您从第二个中间件功能得到了响应,会怎么样?
类似以下内容:-

app.get('/hello', function (req, res, next) {
  // Your piece of logic
  next();
});

app.get('/hello', function (req, res, next) {
  res.send("Hello !!!!");
});

在这种情况下,您需要调用两个中间件函数。因此,到达第二个中间件函数的唯一方法是调用next();
如果你不调用next呢?不要指望第二个中间件函数会自动被调用。在调用第一个函数后,你的请求将被挂起。第二个函数将永远不会被调用,你也不会得到响应。

ulydmbyx

ulydmbyx4#

Next用于将控制传递给下一个中间件功能。如果不是,则请求将保持挂起或打开状态。

sz81bmfz

sz81bmfz5#

我还想补充一下为什么express不调用下一个中间件,而是让我们控制它。由于节点是异步的,如果express调用下一个中间件而不等待某个异步调用完成,那么响应可能不完整或包含错误,因此用户可以控制何时应该调用下一个中间件函数。

g6ll5ycj

g6ll5ycj6#

expressapphandle函数的主要作用是向客户端发送一个响应,并终止request-response cycle。而终止这个循环可以由response methods中的一个来完成(例如,资源end()、资源json这意味着如果中间件或route handler执行了一些操作,但随后没有调用response methods中的一个,也没有将控制传递给下一个处理程序或中间件,request-response cycle将不会被终止。但是next的作用取决于它被调用的位置和方式。
为了管理不同的任务(路由处理器、中间件),express创建了stacks。它们看起来像是tasksqueue。每个routerroute创建了它自己的tasksstack;
函数的use方法将taskmiddleware函数)添加到routerstack中。app.getapp.post等创建单独的route(具有其自己的stack,并将route的实际handlers推送到它)在router中,然后将这些route处理程序推送到函数中 Package 的router。也就是说,当在routerstack中创建了一个route时,类似于routetask( Package 函数),并推送了subtasks

// pushes task to the router stack
app.use((req, res, next) => {
    console.log('log request');
    next();
});

// creates route, route stack, 
// pushes tasks to the route stack,
// wraps tasks in a function (another task)
// pushes wrapper function to the
// router stack
app.get('/', (req, res, next) => {
    res.send('Hello World');
});

因为router有它自己的stack,所以不带参数调用next只能得到route的下一个处理程序:

app.get('/', 
    (req, res, next) => {
        console.log('first handler');
        // passes the control to the second handler
        next();
    },
    (req, res, next) => {
        console.log('first handler');
        res.send('Hello World');
    }
);

middleware内调用nextexpress建议应用use方法来挂载middleware)会使我们进入router的下一个routemiddleware,因为middleware(挂载时)被推送到routerstack
next接受不同的参数。任何不是'route''router'的参数都将被视为错误,并将被传递给errormiddleware,该error必须在所有路由之后装载,并具有四个参数:

// error handling middleware
app.use((error, req, res, next) => {
    res.status(error.status || 500);
    res.send(error.message || 'Server internal error');
});

将字符串'route'作为next的参数,将跳过所有剩余的routehandlers,并获取router的下一个route

app.get('/', 
    (req, res, next) => {
        console.log('first handler');
        // passes control to the next route
        next('route');
    },
    (req, res, next) => {
        // this handler will be skipped
        next();
    }
);

app.get('/',
    (req, res, next) => {
        // this route will be called at the end
        res.send('Hello World');
    }
);

将字符串'router'作为next的参数,使我们脱离当前的router

// router 'one'
app.get('/', 
    (req, res, next) => {
        console.log('first handler');
        // passes control to the next router
        next('router');
    },
    (req, res, next) => {
        // this handler will be skipped
        next();
    }
);

// router 'two'
app.get('/',
    (req, res, next) => {
        // this route will be called at the end
        res.send('Hello World');
    }
);

相关问题