淺析Express中的路由與應(yīng)用模式

1. ? 引言

? ? ? ? Express是一個(gè)基于Node.js的輕量級(jí)web開發(fā)框架,具有體積小,使用靈活等特點(diǎn)。查看Express的源碼熄攘,如果不計(jì)供使用的中間件,主體框架只有一千余行代碼垃它,非常簡(jiǎn)練鲜屏。

? ? ? ? Express模型的核心為Express中定義的路由和路由器烹看。分析Express源碼可發(fā)現(xiàn)Express的路由提供多種靈活的應(yīng)用模式国拇。

? ? ? ? ?我們首先介紹一下Express中的路由、路由器相關(guān)概念惯殊、結(jié)構(gòu)及其特點(diǎn)酱吝,然后針對(duì)典型場(chǎng)景描述使用Express路由的四種應(yīng)用模式。

2. Express中的路由與路由器

? ? ? ? Express具有典型的MVC模型特征土思。我們將路由定義為一個(gè)二元組route=(path, endpoint):其中path為HTTP請(qǐng)求的路徑务热,endpoint為請(qǐng)求路徑應(yīng)映射到的端點(diǎn)(端點(diǎn)可視為處理該請(qǐng)求的實(shí)體),則Express中的路由器負(fù)責(zé)將請(qǐng)求映射到對(duì)應(yīng)端點(diǎn)進(jìn)行處理。

? ? ? ?Express中的路由器分為兩種類型:

? ? ? ?app類型的路由器常使用如下代碼創(chuàng)建:


var express =require('express');

var app = express();


? ? ? ? router類型的路由器常使用如下代碼創(chuàng)建:


??var express = require('express');

var router =express.Router();


? ? ? ? ? app和router是形為function(request, response, next)形式的函數(shù)對(duì)象己儒,使用app.verb()崎岂,router.verb()形式函數(shù)實(shí)現(xiàn)路由注冊(cè)(路由注冊(cè)本質(zhì)上是一個(gè)觀察者模式)。

? ? ? ? app.verb()和router.verb()中的verb常使用use闪湾、get冲甘、post、put途样、delete江醇、route等動(dòng)詞,不同動(dòng)詞管轄的HTTP請(qǐng)求方法范圍不同何暇,這些動(dòng)詞函數(shù)的參數(shù)形式常為(pathExp, handleCallback)形式:其中pathExp表示請(qǐng)求路徑陶夜,可為正則表達(dá)式;handleCallback為路徑映射處理函數(shù)裆站。

? ? ? ?app是Express框架所構(gòu)建程序的請(qǐng)求處理入口条辟,app可作為頂層路由器使用黔夭,在應(yīng)用中也可掛載下級(jí)路由器(使用router對(duì)象)以實(shí)現(xiàn)分級(jí)路由。其間的關(guān)系可由圖1表示:


圖 1

? ? ? ? 考察如下代碼:


app.use('/reports', router1);

router1.get('/querymysql/:id', queryMysqlData,handleQueryData);


? ? ? ? 對(duì)于http請(qǐng)求URL“/reports/querymysql/1”羽嫡,Express中的路由器將此請(qǐng)求路由到queryMysqlData函數(shù)處理纠修。

3. ? Express中的路由應(yīng)用模式

3.1. ?REST模式

? ? ? ? 對(duì)于一個(gè)使用restful風(fēng)格的應(yīng)用, 讓我們想一想在不使用Express的時(shí)候如何在Node.js中處理rest請(qǐng)求,我們常常會(huì)寫下如下示例代碼:


var server = http.createServer(function (request,response) {

???? switch(request.url) {

???????????????????case 'uri1'

???????????????????????handleUri1 (request, response);

???????????????????????break;

???????????????????case ' uri2'

???????????????????????handleU ri2(request, response);

???????????????????????break;

???????????????????case ' uri3'

???????????????????????handleU ri3 (request, response);

???????????????????????break;

???????????????????case ' uri4'

???????????????????????handleU ri4 (request, response);

???????????????????????break;

???????????????????...

???????????????????default:

???????????????????????logToConsole('unknown path:' + path);

???????????????????????response.writeHead(404);

???????????????????????response.end("404 Not found");

???????????????????????break;

???????????????}

}


? ? ? ? Express將上面代碼中對(duì)每個(gè)rest資源的操作(switch分支)轉(zhuǎn)換為路由厂僧,路由中的路徑為rest資源的URI扣草,處理端點(diǎn)為function(request, response,next)形式、對(duì)rest資源的操作函數(shù)颜屠。

? ? ? ? 常使用app.route函數(shù)實(shí)現(xiàn)一個(gè)完整的restful接口辰妙,如下示例代碼:


app.route('/uri1')

? .get(function(req,res) {

??? handleGetUri1();

? })

.post(function(req, res) {

??? handlePost Uri1();

? })

?.put(function(req, res) {

??? handlePut Uri1();

? })

.delete(function(req, res) {

??? handleDeleteUri1();

? });


3.2. ?AOP模式

? ? ? ? 在處理不同路徑的HTTP請(qǐng)求時(shí),常常需要在請(qǐng)求處理前和處理后做一些通用操作甫窟,這種應(yīng)用需求是一個(gè)典型的AOP應(yīng)用要求密浑。

? ? ? ? Express中允許定義一個(gè)具有通配路徑的路由,在調(diào)用其它路徑的路由前會(huì)先調(diào)用該通配路徑路由粗井。此通配路徑路由也成為其它路徑路由切面的一個(gè)注入點(diǎn)尔破,考察如下示例代碼:


router.use(function timelog(req,res,next){

???console.log("receive report request time is:",Date.now());

??? next(); //注意next函數(shù)的使用,必須聲明該函數(shù)才能調(diào)用后繼映射函數(shù)

});

router.get('/chart1', proxy({

??? target:'http://127.0.0.1:8082',

???changeOrigin: true,

??? pathRewrite:{

???????'^/reports/chart1': '/loadChart1'

??? }

}));?

router.get('/querymysql/:id', queryMysqlData,handleQueryData);


? ? ? ? 上述代碼中浇衬,在執(zhí)行router的'/chart1'路由和'/querymysql/:id'路由之前都會(huì)執(zhí)行timelog函數(shù)懒构,在日志中記錄當(dāng)前路由執(zhí)行時(shí)間。

3.3. ? 責(zé)任鏈模式

? ? ? ? 在Node.js中耘擂,由于多使用異步函數(shù)胆剧,常會(huì)出現(xiàn)異步回調(diào)函數(shù)中嵌套異步回調(diào)函數(shù)的情形。當(dāng)出現(xiàn)多重異步回調(diào)時(shí),則代碼會(huì)變得混亂和難以維護(hù)醉冤。

? ? ? ? 考察一個(gè)應(yīng)用場(chǎng)景:應(yīng)用需要在數(shù)據(jù)庫(kù)中進(jìn)行多次查詢秩霍,并對(duì)多次查詢的結(jié)果綜合處理。若使用數(shù)據(jù)庫(kù)提供的異步查詢接口蚁阳,則需要在前一個(gè)查詢操作的回調(diào)函數(shù)中進(jìn)行下一個(gè)查詢操作铃绒,若寫在一個(gè)回調(diào)函數(shù)中,代碼顯臃腫螺捐。

? ? ? ? Express的一個(gè)路由可定義多個(gè)處理函數(shù)颠悬,這些處理函數(shù)可設(shè)計(jì)為鏈?zhǔn)秸{(diào)用,實(shí)現(xiàn)了責(zé)任鏈模式归粉,考察如下代碼:


app.get('/test',function(req,res,next){

??? handle1();

??? next();

},function(req,res,next){

??? handle2();

??? next();

},function(req,res,next){

?? handle3();

});


? ? ? ? 上述代碼中:handle1, handle2, handle3構(gòu)成了一個(gè)處理責(zé)任鏈“handle1->handle2->handle3”椿疗,通過next函數(shù)指引鏈?zhǔn)秸{(diào)用。

? ? ? ? Express中路由的責(zé)任鏈應(yīng)用特性使得多重異步嵌套的代碼變得清晰和優(yōu)雅糠悼。

? ? ? ? ?針對(duì)本節(jié)開始提到的數(shù)據(jù)庫(kù)查詢應(yīng)用場(chǎng)景届榄,下面的示例代碼展示了責(zé)任鏈模式的應(yīng)用特點(diǎn)。


router.get('/querymysql/:id',queryMysqlData, handleQueryData);

//查詢mysql表中的數(shù)據(jù)

functionqueryMysqlData(req, res, next) {

??? if ("id" in req.params) {

??????? dbpool.query(" select * fromarticles where id=?" ,[req.params.id], function (err, rows, fields) {

??????????? if (err) {

??????????????? res.send(err.stack);

??????????? } else {

??????????????? if (rows && rows.length> 0) {

??????????????????? console.log('The queriedrows is: ', rows.length);

??????????????????? res.articles = rows;

??????????????????? next();

??????????????? } else {

??????????????????? res.send("no queryresults!");

??????????????? }

??????????? }

??????? });

??? } else {

??????? res.send("invalid queryparams!");

??? }

}

//處理查詢mysql后得到的數(shù)據(jù)

functionhandleQueryData(req, res) {

??? if ("articles" in res) {

??????? res.send("id:" +req.params.id + ";title:" + res.articles[0].title);

??? }else{

??????? res.send("no query datahandled!");

??? }

}


3.4. ?熔斷器模式

? ? ? ? 上節(jié)提到的責(zé)任鏈模式本質(zhì)上是一個(gè)逐級(jí)調(diào)用模型倔喂。在分布式服務(wù)架構(gòu)(微服務(wù)架構(gòu))中铝条,深度調(diào)用常常需要考慮調(diào)用可達(dá)性問題靖苇,即需要考慮某級(jí)調(diào)用會(huì)否一直不響應(yīng)。調(diào)用可達(dá)性問題常使用熔斷器模式班缰,即在調(diào)用端設(shè)置一個(gè)熔斷器贤壁,熔斷條件產(chǎn)生時(shí),熔斷器發(fā)生熔斷埠忘,返回給調(diào)用方調(diào)用失敗信息脾拆。

? ? ? ? 考慮這樣的應(yīng)用場(chǎng)景:對(duì)于一些有處理時(shí)間要求的請(qǐng)求,當(dāng)在指定時(shí)間內(nèi)沒有完成處理莹妒,需要向請(qǐng)求方返回處理失敗信息名船。針對(duì)此應(yīng)用場(chǎng)景,可在Express路由中設(shè)置超時(shí)熔斷器旨怠,當(dāng)處理超時(shí)渠驼,開啟熔斷器,通知請(qǐng)求方本次處理請(qǐng)求失敗鉴腻。

? ? ? ? 上述應(yīng)用場(chǎng)景可使用如下示例代碼應(yīng)對(duì):


app.get('/circuit',function(req, res, next){

?? ?var bt=setTimeout(function () {

???????next('route'); //觸發(fā)熔斷

??? },3000); //設(shè)置熔斷時(shí)間為3

?? res.breakTimer= bt;

?? next();

},function(req,res,next){

??? handle2();

??? next();

},function(req,res,next){

?? handle3();

?? clearTimeout(res.breakTimer);//正常執(zhí)行完畢迷扇,取消熔斷定時(shí)器

});

app.get('/circuit ',function(req,res,next){

? if(!res.finished){//如果還沒有響應(yīng),啟動(dòng)熔斷

??? //返回給調(diào)用者熔斷信息

???res.send("breakCondition is true, notify the invoker.");

? }

});


4. ? 小結(jié)

? ? ? ? 本文介紹了Express框架中路由和路由器的概念爽哎、結(jié)構(gòu)和特點(diǎn)蜓席,并針對(duì)典型應(yīng)用場(chǎng)景歸納了REST、AOP倦青、責(zé)任鏈瓮床、熔斷器四種應(yīng)用模式,可用于應(yīng)用開發(fā)中的一些常用場(chǎng)景产镐。

5. ?參考文獻(xiàn)

1.http://expressjs.com/

2.http://www.runoob.com/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市踢步,隨后出現(xiàn)的幾起案子癣亚,更是在濱河造成了極大的恐慌,老刑警劉巖获印,帶你破解...
    沈念sama閱讀 221,406評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件述雾,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡兼丰,警方通過查閱死者的電腦和手機(jī)玻孟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,395評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鳍征,“玉大人黍翎,你說我怎么就攤上這事⊙薮裕” “怎么了匣掸?”我有些...
    開封第一講書人閱讀 167,815評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵趟紊,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我碰酝,道長(zhǎng)霎匈,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,537評(píng)論 1 296
  • 正文 為了忘掉前任送爸,我火速辦了婚禮铛嘱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘袭厂。我一直安慰自己弄痹,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,536評(píng)論 6 397
  • 文/花漫 我一把揭開白布嵌器。 她就那樣靜靜地躺著肛真,像睡著了一般。 火紅的嫁衣襯著肌膚如雪爽航。 梳的紋絲不亂的頭發(fā)上蚓让,一...
    開封第一講書人閱讀 52,184評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音讥珍,去河邊找鬼历极。 笑死,一個(gè)胖子當(dāng)著我的面吹牛衷佃,可吹牛的內(nèi)容都是我干的趟卸。 我是一名探鬼主播,決...
    沈念sama閱讀 40,776評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼氏义,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼锄列!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起惯悠,我...
    開封第一講書人閱讀 39,668評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤邻邮,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后克婶,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體筒严,經(jīng)...
    沈念sama閱讀 46,212評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,299評(píng)論 3 340
  • 正文 我和宋清朗相戀三年情萤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了鸭蛙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,438評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡筋岛,死狀恐怖娶视,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情泉蝌,我是刑警寧澤歇万,帶...
    沈念sama閱讀 36,128評(píng)論 5 349
  • 正文 年R本政府宣布揩晴,位于F島的核電站,受9級(jí)特大地震影響贪磺,放射性物質(zhì)發(fā)生泄漏硫兰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,807評(píng)論 3 333
  • 文/蒙蒙 一寒锚、第九天 我趴在偏房一處隱蔽的房頂上張望劫映。 院中可真熱鬧,春花似錦刹前、人聲如沸泳赋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,279評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)祖今。三九已至,卻和暖如春拣技,著一層夾襖步出監(jiān)牢的瞬間千诬,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,395評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工膏斤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留徐绑,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,827評(píng)論 3 376
  • 正文 我出身青樓莫辨,卻偏偏與公主長(zhǎng)得像傲茄,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子沮榜,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,446評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容