当前位置:首页 > 日记 > 正文

nodejs对express中next函数的一些理解

nodejs对express中next函数的一些理解

最近公司在使用node做前后端分离,采用的web框架是express,所以对express框架进行了深入的了解,前段时间写了篇关于express路由的文章,但是在那篇文章中貌似少了一个很重要的内容,就是express的next,所以今天单独来说说express的next。

关于next主要从三点来进行说明:

  • next的作用是什么?
  • 我们应该在何时使用next?
  • next的内部实现机制是什么?

Next的作用

我们在定义express中间件函数的时候都会将第三个参数定义为next,这个next就是我们今天的主角,next函数主要负责将控制权交给下一个中间件,如果当前中间件没有终结请求,并且next没有被调用,那么请求将被挂起,后边定义的中间件将得不到被执行的机会。

何时使用Next

从上边的描述我们已经知道,next函数主要是用来确保所有注册的中间件被一个接一个的执行,那么我们就应该在所有的中间件中调用next函数,但有一个特例,如果我们定义的中间件终结了本次请求,那就不应该再调用next函数,否则就可能会出问题,我们来看段代码

app.get('/a', function(req, res, next) {  res.send('sucess');  next();});// catch 404 and forward to error handlerapp.use(function(req, res, next) { console.log(404); var err = new Error('Not Found'); err.status = 404; next(err);});app.use(function(err, req, res, next) { res.status(err.status || 500); res.render('error', {  message: err.message,  error: {} });});

发送请求"/a",控制台打印日志如下:

404GET /a 500 6.837 ms - -Error: Can't set headers after they are sent.  at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:345:11)

为什么代码会抛异常呢,就是因为我们在res.send之后调用了next函数,虽然我们本次的请求已经被终止,但后边的404中间件依旧会被执行,而后边的中间件试图去向res的headers中添加属性值,所以就会抛出上边的异常。

读到这你可能会有个疑问,如果我不在res.send后边调用next函数,那后边定义的404中间件是不是永远都不会被执行到。现在我们删除res.send后边next函数调用,发送请求"/xxx",我们就会发现404中间件被执行了,(ㄒoㄒ),这不是和我们之前说的矛盾了吗,我们的自定义中间件没有调用next,但后边定义的中间件仍旧被执行了,这究竟是为什么呢。看来只能求助源码了~~~

Next的内部机制

function next(err) {  ... //此处源码省略  // find next matching layer  var layer;  var match;  var route;  while (match !== true && idx < stack.length) {   layer = stack[idx++];   match = matchLayer(layer, path);   route = layer.route;   if (typeof match !== 'boolean') {    // hold on to layerError    layerError = layerError || match;   }   if (match !== true) {    continue;   }   ... //此处源码省略  } ... //此处源码省略  // this should be done for the layer  if (err) {    layer.handle_error(err, req, res, next);  } else {   layer.handle_request(req, res, next);  } }

上边就是express中next的源码,为了更容易说明问题,对代码进行了删减。从上边的源码可以发现,next函数内部有个while循环,每次循环都会从stack中拿出一个layer,这个layer中包含了路由和中间件信息,然后就会用layer和请求的path就行匹配,如果匹配成功就会执行layer.handle_request,调用中间件函数。但如果匹配失败,就会循环下一个layer(即中间件)。

现在我们就能解释上边提出的问题了,为什么我们的自定义中间件中没调用next函数,但后边的404中间件仍旧会被执行到,因为我们请求的"/xxx"匹配不到我们注册的"/a"路由中间件,所以while循环会继续往下执行,匹配404中间件成功,所以会执行404中间件。

 注意:app.use注册的中间件,如果path参数为空,则默认为"/",而path为"/"的中间件默认匹配所有的请求。

有一点需要特别指出,其实我们在定义路由中间件的时候函数的第三个参数next和我们定义非路由中间件的函数的第三个参数next不是同一个next,我们在上边看到的是非路由中间件的next,而路由中间件的next函数是这样的

function next(err) {  if (err && err === 'route') {   return done();  }  var layer = stack[idx++];  if (!layer) {   return done(err);  }  if (layer.method && layer.method !== method) {   return next(err);  }  if (err) {   layer.handle_error(err, req, res, next);  } else {   layer.handle_request(req, res, next);  } }

这个next比上边的那个next要简单很多,它负责同一个路由的多个中间件的控制权的传递,并且它会接收一个参数"route",如果调用next(“route”),则会跳过当前路由的其它中间件,直接将控制权交给下一个路由。

最后有必要再说一说next(err),next(err)是如何将控制权传递到错误处理中间件的,从前边的代码我们知道,当调用next(err)是,express内部会调用layer.handle_error,那我们来看看它的源码

Layer.prototype.handle_error = function handle_error(error, req, res, next) { var fn = this.handle; if (fn.length !== 4) {  // not a standard error handler  return next(error); } try {  fn(error, req, res, next); } catch (err) {  next(err); }};

代码中的fn就是中间件函数,express会对fn的参数个数进行判断,如果参数个数不等于4则认为不是错误处理中间件,则继续调用next(err),这样就会进入到下一个中间件函数,继续进行参数个数判断,如此方式一直到某个中间件函数的参数个数是4,就认为找到了错误处理中间件,然后执行此中间件函数。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

相关文章

Fireworks视图模式怎么切换使用?

Fireworks视图模式怎么切换使用?

模式,视图,电脑软件,Fireworks,Fireworks怎么切换视图模式,这个功能可能我们现在一直在用ps估计也没有这个功能。软件名称:fireworks8简体中文版 (含序列号)软件大小:88MB更新时间:2014-09-091、直接来到Fireworks根目录2、直接运行我的 Firewo…

Word2016设置不同页边距有什么方法

Word2016设置不同页边距有什么方法

设置,边距,方法,教程,有什么,  在文档中,我们经常要设置不同的页边距,有时候还需要自定义页边距,这样可以很好的排版。排版中时常需要设置页边距,这样可以让排版更加美观整洁。以下是小编为您带来的关于Word2016设置不同页边距的教程,希望对…

nodejs实现发出蜂鸣声音 | 系统报

nodejs实现发出蜂鸣声音 | 系统报

方法,系统,声音,蜂鸣,电脑软件,本文实例讲述了nodejs实现发出蜂鸣声音(系统报警声)的方法。分享给大家供大家参考,具体如下:nodejs 有时候需要发出报警声音,使用以下代码可以调用系统蜂鸣器发声process.stdout.write('\x07')另外附上我自己…

防止页面url缓存中ajax中post请求

防止页面url缓存中ajax中post请求

请求,处理方法,缓存,页面,电脑软件,防止页面url缓存中ajax中post请求的处理方法 一般我们在开发中经常会用到Ajax请求,异步发送请求,然后获取我们想要的数据,在Ajax中使用Get请求数据不会有页面缓存的问题,而使用POST请求可是有时候页面会缓…

ps处理如何设置颜色

ps处理如何设置颜色

方法,设置,颜色,如何设置,图片,  大家处理图片时一定要设置颜色,这样我们在ps中才可以方便使用。那么大家知道ps处理图片如何设置颜色吗?下面是小编整理的ps处理图片如何设置颜色的方法,希望能帮到大家!ps处理图片设置颜色的方法我们先是要…

Excel2007的新功能有哪些

Excel2007的新功能有哪些

新功能,有哪些,电脑软件,  Excel2007目前在市场使用率还是蛮高的,作为Excel2007的开版之作,还是要给大家说说Excel2007的新功能。以下是小编为您带来的关于Excel2007的新功能介绍,希望对您有所帮助。Excel2007的新功能介绍Excel 2007较之以…

在Word2007中如何新建样式在Word20

在Word2007中如何新建样式在Word20

步骤,方法,样式,电脑软件,strong,  在Word2007的空白文档窗口中,用户可以新建一种全新的样式。例如新的表格样式、新的列表样式等,操作步骤如下所述:下面由小编为您提供在Word2007中新建样式的技巧,希望能帮助您。Word2007中新建样式步骤第1…

Angular中$broadcast和$emit的使用

Angular中$broadcast和$emit的使用

使用方法,详解,电脑软件,Angular,broadcast,要在控制器之间传递变量变化需要使用angular中的$broadcast和$emit方法来传递,同时使用$on来接收事件并作出响应。broadcast译为广播,即上级传递下级。 示例代码:<script src="../angular.js"></s…

AngularJs 延时器、计时器实例代码

AngularJs 延时器、计时器实例代码

实例代码,计时器,延时器,电脑软件,AngularJs,1.$timeout延时器apptest.controller("main",function($scope,$timeout){ $scope.pink="pink"; $scope.box="第二个盒子"; $timeout(function(){ $scope.pink="第一个盒子内容,延迟…

简单谈谈require模块化jquery和ang

简单谈谈require模块化jquery和ang

模块化,简单,电脑软件,require,jquery,require 模块化开发问题,正常自己写的模块 是exports 导出一个模块//模块化引入jquery 不同和问题require 引入jquery swiper .... 插件和库的时候需要require.config({  baseUrl:"js/libs", //文…

excel表格如何变成白色背景

excel表格如何变成白色背景

方法,设置,表格,背景,白色,  如何设置EXCEL表格中的表格背景呢?常用Excel的朋友看腻了白色背景想跟换怎么吧?接下来小编举例简单的例子告诉大家Excel表格设置背景的方法吧。Excel表格设置背景的方法首先打开excel表格,做一个简单表格。Exc…

浅谈pc端rem字体设置的问题

浅谈pc端rem字体设置的问题

字体设置,浅谈,电脑软件,rem,pc,1、内容在一屏内显示的,采用了(内容框)上下左右居中的办法,里面的内容绝对于这个内容框定位.这样一来,在不同大小屏中,内容总是在中间,看起来较正常2、长,宽,LEFT,TOP,RIGHT,BOTTOM都采用了REM,并且HTML的FONT-SIZE设置…