express 中間件原理

有時候面試,老是被問到一些自己使用過的技術,但是僅僅限於使用,這個是真沒深入研究過,鄙人能力有限的緣故吧。但是呢,每每被問倒之後下來又會想為什麼不在深入一點呢?(邪惡😈之笑)~~;完全可以再多了解一點嘛。

不過怎麼說呢,面試官需要的是一類人才,他也會問到旁系不相干或者在深層次的問題,這點我就覺得有點奇怪了,甲方需要的能力滿足,關於其他的不會反而會減分,難道不應該是,不會不要緊,有時間多學習學習就好了嗎。非要搞得難倒面試者才甘心。既然這樣的面試官很多,所以能多學點是一點,高標準要求自己。

那天是阿里的面試官電話面試的,也僅限于溝通,額~~,被阿里電話面試了好多次,都沒結果,我想說的是遲早我都會進來,浪費大家時間幹嘛。

前幾個問題還好,都是回答自己擅長的問題,還算OK。我的站點有一個是花夏集。他是看到這個聯繫到我的。一上來就問我是不是 花夏集 搞得有點一頭霧水。然後就解釋是怎麼看到我的,找到我的。瞬間很奇怪加驚動。對!就是驚動。居然有人因為看到一個技術人員的詩集站點從而打電話進行面試的。可能是由於這個站點時nodejs+express+mongodb做的吧。事實證明確實是因為這樣,他們剛好需要nodejs方面的工程師。

巴拉巴拉...聊了一些,最後聊到koa中間件,這個我不會,koa沒用過,用過express.僅限於參照文檔做的,然後就開始了......對於express深入的確實不了解,聞到中間件事一問三不知,只知道怎麼用,簡單的自己實現原理都不清楚,所以現在才開始寫一篇文章來說明下。也是看了網上的文章解釋,有了進一步的了解。

廢話了這麼多,進入正題:express中間件原理簡單剖析

express首先得會點nodejs吧,先來一段nodejs最基礎的hello world

var http = require('http');
http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('Hello World\n');
}).listen(1337, '127.0.0.1');

上面这段代码来自nodejs的官网,非常简单,就是来一个请求,就用传给createServer的匿名函数来处理请求。

繼續看看Express的代码

var app = express();
//...中间忽略
http.createServer(app).listen(app.get('port'), function(){
    console.log('Express server listening on port ' + app.get('port'));
});

对比可以看出,执行express()后,会返回一个函数,赋值给app,app應該是:

function(req,res){//...}

然后请求都会被app这个函数处理(因为这个app是执行express后的结果)

可以认为,在express内部,有一个函数的数组,暂时叫这个数组tasks,每来一个请求express内部会依次执行这个数组中的函数(这里说依次并不严谨,每个函数必须满足一定条件才行,这个后面说),应该可以想到,在这个函数数组里,每个函数的签名应该像下面那样實際上這個就是中間件的精髓所在...噓!

function(req, res){//...}

實際上應該是:

function(req, res, next){//...}

这里的next,是指下一个函数。后面我们会写一些试验来体验一下这个next.

1.导入相关模块

2.执行过 var app = express() 后,使用app.set 设置express内部的一些参数(options)使用app.use 来注册函数,可以简单的认为是向那个tasks的数组进行push操作

3.通过http.createServer 用app来处理请求

试验1. 向express中注册自定义函数

注册进express中的函数:

  1. 长成下面这个样子
function(req, res, next){
    //...我们自己的逻辑
    next();
}

或者:

app.use(function(err,req,res,next){
    if(err){
        //自己的处理错误的逻辑
        console.log(err.message);
        console.log(err.stack);
        res.end('404')  
    }
});
  1. app.use(customerFunc) 要写在下面两句的前面
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));

一般情況下是這麼使用的,但是對於通用error處理可以放到最後,http.createServer之前

app.use(express.static(path.join(__dirname, '/web/dist')));
// catch 404 and forwarding to error handler
app.use(function(req, res, next) {
    var err = new Error('Not Found');
    err.status = 404;
    next(err);
});
// 启动及端口
http.createServer(app).listen(app.get('port'), function(req, res){
    console.log('启动成功,端口为' + app.get('port'));
    console.log('主页地址:http://localhost:' + app.get('port'));
});

关于第2点,是因为路由后或请求静态资源后,一次请求响应的生命周期实质上已经结束,加在这后面进行请求处理,没有任何意义。

关于第1点,写点代码繼續進行試驗:

app.use(function(req,res,next){
    console.log("111");
    next();
});

如果不写next(),那么后面注册的函数就不会执行(一個大臉疑問),运行測試下不就知道了

app.use(function(req,res,next){
    console.log('111');
    next();
    console.log('222');
});

app.use(function(req,res,next){
    console.log("333");
    next();
});

那么控制台的输出的顺序是:111 333 222

试验二 next()的工作原理

整个处理请求的模型还是比較简单的,在理解的上面的过程后,能不能不借助express,自己实现上面的过程呢,主要是怎么处理next()那一块

var http = require('http');
function express(){
    var funcs = [];

    var expr = function(req,res){
        var i = 0;
        function next(){            
            var task = funcs[i++];
            if(!task) return;
            task(req,res,next);
        }
        next();
    }
    expr.use=function(f){
        funcs.push(f);
    }
    return expr;
}
var app = express();

app.use(function(req,res,next){
    console.log('haha');
    next();
});
app.use(function(req,res,next){
    console.log('hehe');
    next();
});
app.use(function(req,res){
    res.end("there is nothing happened");
});

http.createServer(app).listen('3000', function(){
  console.log('Express server listening on port 3000');
});

启动服务后,每来一个请求,控制台会依次输出haha hehe,然后浏览器是there is nothing happened

当然如果要更深一步了解到,可以去看源代码,实际上这一部分的主要代码是在connect中的,在connect/lib/proto.js 这个源文件中,主要是app.use,和app.handle 两个函数中

後續會再次研究expressapp.use原理。

原文鏈接
參考原文來自 前端亂燉

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,036评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,046评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,411评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,622评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,661评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,521评论 1 304
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,288评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,200评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,644评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,837评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,953评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,673评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,281评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,889评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,011评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,119评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,901评论 2 355

推荐阅读更多精彩内容