(七)Connect模塊解析(之一)

2018-02-24 16:10 更新

Connect模塊背景

Node.js的愿望是成為一個(gè)能構(gòu)建高速,可伸縮的網(wǎng)絡(luò)應(yīng)用的平臺(tái),它本身具有基于事件,異步,非阻塞,回調(diào)等特性,這在前幾篇專(zhuān)欄中有過(guò)描述。正是基于這樣的一些特性,Node.js平臺(tái)上的Web框架也具有不同于其他平臺(tái)的一些特性,其中Connect是眾多Web框架中的佼佼者。
Connect在它的官方介紹中,它是Node的一個(gè)中間件框架。超過(guò)18個(gè)捆綁的中間件和一些精選第三方中間件。盡管Connect可能不是性能最好的Node.jsWeb框架,但它卻幾乎是最為流行的Web框架。為何Connect能在眾多框架中勝出,其原因不外乎有如下幾個(gè):

  • 模型簡(jiǎn)單
  • 中間件易于組合和插拔
  • 中間件易于定制和優(yōu)化
  • 豐富的中間件

Connect自身十分簡(jiǎn)單,其作用是基于Web服務(wù)器做中間件管理。至于如何如何處理網(wǎng)絡(luò)請(qǐng)求,這些任務(wù)通過(guò)路由分派給管理的中間件們進(jìn)行處理。它的處理模型僅僅只是一個(gè)中間隊(duì)列,進(jìn)行流式處理而已,流式處理可能性能不是最優(yōu),但是卻是最易于被理解和接受。基于中間件可以自由組合和插拔的情況,優(yōu)化它十分容易。
Connect模塊目前在NPM倉(cāng)庫(kù)的MDO(被依賴(lài)最多的模塊)排行第八位。但這并沒(méi)有真實(shí)反映出它的價(jià)值,因?yàn)榕判械谖逦坏腅xpress框架實(shí)際上是依賴(lài)Connect創(chuàng)建而成的。關(guān)于Express的介紹,將會(huì)在后續(xù)的專(zhuān)欄中一一為你講解。

中間件

讓我們回顧一下Node.js最簡(jiǎn)單的Web服務(wù)器是如何編寫(xiě)的:

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');

我們從最樸素的Web服務(wù)器處理流程開(kāi)始,可以看到HTTP模塊基于事件處理網(wǎng)絡(luò)訪問(wèn)無(wú)外乎兩個(gè)主要的因素,請(qǐng)求和響應(yīng)。同理的是Connect的中間件也是扮演這樣一個(gè)角色,處理請(qǐng)求,然后響應(yīng)客戶(hù)端或是讓下一個(gè)中間件繼續(xù)處理。如下是一個(gè)中間件最樸素的原型:

function (req, res, next) {
  // 中間件
}

在中間件的上下文中,有著三個(gè)變量。分別代表請(qǐng)求對(duì)象、響應(yīng)對(duì)象、下一個(gè)中間件。如果當(dāng)前中間件調(diào)用了res.end()結(jié)束了響應(yīng),執(zhí)行下一個(gè)中間件就顯得沒(méi)有必要。

流式處理

為了演示中間件的流式處理,我們可以看看中間件的使用形式:

var app = connect();
// Middleware
app.use(connect.staticCache());
app.use(connect.static(__dirname + '/public'));
app.use(connect.cookieParser());
app.use(connect.session());
app.use(connect.query());
app.use(connect.bodyParser());
app.use(connect.csrf());
app.use(function (req, res, next) {
  // 中間件
});
app.listen(3001);

Conncet提供use方法用于注冊(cè)中間件到一個(gè)Connect對(duì)象的隊(duì)列中,我們稱(chēng)該隊(duì)列叫做中間件隊(duì)列。

中間件隊(duì)列

Conncet的部分核心代碼如下,它通過(guò)use方法來(lái)維護(hù)一個(gè)中間件隊(duì)列。然后在請(qǐng)求來(lái)臨的時(shí)候,依次調(diào)用隊(duì)列中的中間件,直到某個(gè)中間件不再調(diào)用下一個(gè)中間件為止。

app.stack = [];
app.use = function(route, fn){
  // …

  // add the middleware
  debug('use %s %s', route || '/', fn.name || 'anonymous');
  this.stack.push({ route: route, handle: fn });

  return this;
};

值得注意的是,必須要有一個(gè)中間件調(diào)用res.end()方法來(lái)告知客戶(hù)端請(qǐng)求已被處理完成,否則客戶(hù)端將一直處于等待狀態(tài)。
流式處理也是Node.js中用于流程控制的經(jīng)典模式,Connect模塊是典型的應(yīng)用了它。流式處理的好處在于,每一個(gè)中間層的職責(zé)都是單一的,開(kāi)發(fā)者通過(guò)這個(gè)模式可以將復(fù)雜的業(yè)務(wù)邏輯進(jìn)行分解。

路由

從前文可以看到其實(shí)app.use()方法接受兩個(gè)參數(shù),route和fn,既路由信息和中間件函數(shù),一個(gè)完整的中間件,其實(shí)包含路由信息和中間件函數(shù)。路由信息的作用是過(guò)濾不匹配的URL。請(qǐng)求在遇見(jiàn)路由信息不匹配時(shí),直接傳遞給下一個(gè)中間件處理。
通常在調(diào)用app.use()注冊(cè)中間件時(shí),只需要傳遞一個(gè)中間件函數(shù)即可。實(shí)際上這個(gè)過(guò)程中,Connect會(huì)將/作為該中間件的默認(rèn)路由,它表示所有的請(qǐng)求都會(huì)被該中間件處理。
中間件的優(yōu)勢(shì)類(lèi)似于Java中的過(guò)濾器,能夠全局性地處理一些事務(wù),使得業(yè)務(wù)邏輯保持簡(jiǎn)單。
任何事物均有兩面性,當(dāng)你調(diào)用app.use()添加中間件的時(shí)候,需要考慮的是中間件隊(duì)列是否太長(zhǎng),因?yàn)槊恳粚又虚g件的調(diào)用都是會(huì)降低性能的。為了提高性能,在添加中間件的時(shí)候,如非全局需求的,盡量附帶上精確的路由信息。
以multipart中間件為例,它用于處理表單提交的文件信息,相對(duì)而言較為耗費(fèi)資源。它存在潛在的問(wèn)題,那就是有可能被人在客戶(hù)端惡意提交文件,造成服務(wù)器資源的浪費(fèi)。如果不采用路由信息加以限制,那么任何URL都可以被攻擊。

app.use("/upload", connect.multipart({ uploadDir: path }));

加上精確的路由信息后,可以將問(wèn)題減小。

MVC目錄

借助Connect可以自由定制中間件的優(yōu)勢(shì),可以自行提升性能或是設(shè)計(jì)出適合自己需要的項(xiàng)目。Connect自身提供了路由功能,在此基礎(chǔ)上,可以輕松搭建MVC模式的框架,以達(dá)到開(kāi)發(fā)效率和執(zhí)行效率的平衡。以下是筆者項(xiàng)目中采用的目錄結(jié)構(gòu),清晰地劃分目錄結(jié)構(gòu)可以幫助劃分代碼的職責(zé),此處僅供參考。

├── Makefile // 構(gòu)建文件,通常用于啟動(dòng)單元測(cè)試運(yùn)行等操作
├── app.js // 應(yīng)用文件
├── automation  // 自動(dòng)化測(cè)試目錄
├── bin  // 存放啟動(dòng)應(yīng)用相關(guān)腳本的目錄
├── conf  // 配置文件目錄
├── controllers  // 控制層目錄
├── helpers  // 幫助類(lèi)庫(kù)
├── middlewares  // 自定義中間件目錄
├── models  // 數(shù)據(jù)層目錄
├── node_modules  // 第三方模塊目錄
├── package.json  // 項(xiàng)目包描述文件
├── public  // 靜態(tài)文件目錄
│?? ├── images  // 圖片目錄
│?? ├── libs  // 第三方前端JavaScript庫(kù)目錄
│?? ├── scripts  // 前端JavaScript腳本目錄
│?? └── styles  // 樣式表目錄
├── test  // 單元測(cè)試目錄
└── views  // 視圖層目錄

參考:

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)