精华 对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 handler
app.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",控制台打印日志如下:
404
GET /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,就认为找到了错误处理中间件,然后执行此中间件函数。
精华 对express中next函数的一些理解的更多相关文章
- [转] 对express中next函数的一些理解
最近公司在使用node做前后端分离,采用的web框架是express,所以对express框架进行了深入的了解,前段时间写了篇关于express路由的文章,但是在那篇文章中貌似少了一个很重要的内容,就 ...
- python中对 函数 闭包 的理解
最近学到 函数 闭包的时候,似懂非懂.迷迷糊糊的样子,很是头疼,今天就特意查了下关于闭包的知识,现将我自己的理解分享如下! 一.python 闭包定义 首先,关于闭包,百度百科是这样解释的: 闭包是指 ...
- delphi中move函数的正确理解(const和var一样,都是传地址,所以Move是传地址,而恰恰不是传值)太精彩了 good
我们能看到以下代码var pSource,pDest:PChar; len: integer;.......................//一些代码Move(pSource,pDest,l ...
- Express ( MiddleWare/中间件 路由 在 Express 中使用模板引擎 常用API
A fast, un-opinionated, minimalist web framework for Node.js applications. In general, prefer simply ...
- 在Express中安装XTemplate
上一节讲了安装Express,并且生成了一个"microblog"的工程,我们的目标是在工程下安装XTemplate: 1.安装xtpl npm install xtpl xtem ...
- node.js 中回调函数callback(转载),说的很清楚,看一遍就理解了
最近在看 express,满眼看去,到处是以函数作为参数的回调函数的使用.如果这个概念理解不了,nodejs.express 的代码就会看得一塌糊涂.比如: 复制代码 代码如下: app.use(fu ...
- 理解express中的中间件
express是轻量灵活的node.js Web应用框架”.它可以帮助你快速搭建web应用.express是一个自身功能极简,完全是由**路由**和**中间件**构成的一个web开发框架,本质上说,一 ...
- 在Express中使用Multiparty进行文件上传及POST、GET参数获取
Express 版本:4.14.1 在Express中,文件上传需要用到multiparty中间件,在项目目录中,通过npm install multiparty –save进行安装必要组件. 前端H ...
- NET MVC全局异常处理(一) 【转载】网站遭遇DDoS攻击怎么办 使用 HttpRequester 更方便的发起 HTTP 请求 C#文件流。 Url的Base64编码以及解码 C#计算字符串长度,汉字算两个字符 2019周笔记(2.18-2.23) Mysql语句中当前时间不能直接使用C#中的Date.Now传输 Mysql中Count函数的正确使用
NET MVC全局异常处理(一) 目录 .NET MVC全局异常处理 IIS配置 静态错误页配置 .NET错误页配置 程序设置 全局异常配置 .NET MVC全局异常处理 一直知道有.NET有相关 ...
随机推荐
- sql 动态行转列
create table u01 (医案编号 varchar(5),药物编号 varchar(5)) insert into u01 select '01','01' union all select ...
- TodoMVC中的Backbone+MarionetteJS+RequireJS例子源码分析之三 Views
这个版本的TodoMVC中的视图组织划分比较细,更加易于理解,这也得益于Marionette为我们带来了丰富的视图选择,原生的backbone只有views,而Marionette则有itemview ...
- addview的使用
之前,使用addview(控件1)的时候 之前是需要给控件1添加一个位置设定,比如是设定在右侧 使用了addrule 但是我想到的是,如果是给一个view 的 后面延长,把该控件放在上面呢?
- Eclipse,到了说再见的时候了——Android Studio最全解析
转自:http://blog.jobbole.com/77635/ 去年的Google大会上,Google带给我们一个小玩具——Android Studio,说它是玩具,是因为它确实比较菜,界面过时, ...
- innerHTML 与 innerText 的区别
innerHTML指的是从对象的起始位置到终止位置的全部内容,包括Html标签.innerText 指的是从起始位置到终止位置的内容,但它去除Html标签.同时,innerHTML 是所有浏览器都支持 ...
- java.lang.NoClassDefFoundError: javax/el/ELResolver 问题解决
HTTP Status 500 - java.lang.NoClassDefFoundError: javax/el/ELResolver type Exception report message ...
- NGUI实现技能CD效果
在NGUI中使用Sprite的遮罩效果可以很轻松的实现技能CD效果. 具体实现步骤: ①新建一个技能图标的Sprite 如图中的Skill001,再在该技能Sprite上添加一个Sprite做遮罩, ...
- WM_COPYDATA进程间通信方案
连续在两个公司使用WM_COPYDATA实现进程间通信了,整理一下 具体步骤: 一. 进程A通过ShellExecute启动进程B, 将用于通信的窗口句柄hWndA(已强转为int值)通过命令行参 ...
- html基本选择符的使用
一.选择符在运用在CSS设计样式时对HTML的指定有至关重要的作用! 二.研究 普通选择符: 1.类型选择符:它可以选择同一个类型的元素! 例如:h1,h2 { color: ...
- Java中的递归运算
Java中的递归运算是一种在自己的方法内部调用自己的方法 递归的设计思想是:把一个复杂的问题,分解为若干个等同的子问题,重复执行,直到之问题能够简单到直接求解,这样复杂的问题就得以解决. 递归运算有两 ...