Node.js —— next( )
来源:互联网 发布:java token编译原理 编辑:程序博客网 时间:2024/06/10 23:08
关于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,就认为找到了错误处理中间件,然后执行此中间件函数。
转载地址:
http://cnodejs.org/topic/5757e80a8316c7cb1ad35bab
- Node.js —— next( )
- Node.js——初识Node.js
- LeetCode——Populating Next Right Pointers in Each Node
- LeetCode——Populating Next Right Pointers in Each Node
- LeetCode——Populating Next Right Pointers in Each Node
- Leetcode——populating-next-right-pointers-in-each-node
- Algorithms—116.Populating Next Right Pointers in Each Node
- LeetCode116—Populating Next Right Pointers in Each Node
- LeetCode117—Populating Next Right Pointers in Each Node II
- Node.js—Node模块机制
- 转载:Node.js入门系列——《深入浅出Node.js》
- node.js学习笔记1——node.js是什么
- node.js 学习笔记——node.js 环境配置
- Node.js入门——mac下安装Node.js
- Node.js 体验 — 当 Node.js 邂逅 Windows Azure
- Node.js开发入门—notepad++ for Node.js
- Node.js基础item1—Node.js概念
- Node.js——梗概
- BZOJ1770: [Usaco2009 Nov]lights 燈(异或方程组)
- HTML5 地理位置
- 想知道最实用的MySQL备份恢复方案?《周四见》吧!
- eclispe工作空间问题
- git生成并添加SSH key
- Node.js —— next( )
- Mac OS X 使用xcode编写连接mysql的C程序
- Canvas 画时钟
- 入门训练 A+B问题
- 数组对象排序
- Canvas 画五角星
- Linux shell 提取文件名和目录名
- [课程设计]Java实现图形化窗口界面可存储ATM机(自助取款机)
- python 生成字符图片