回到正題,現(xiàn)在我們的HTTP服務(wù)器和請(qǐng)求路由模塊已經(jīng)如我們的期望,可以相互交流了,就像一對(duì)親密無間的兄弟。
當(dāng)然這還遠(yuǎn)遠(yuǎn)不夠,路由,顧名思義,是指我們要針對(duì)不同的URL有不同的處理方式。例如處理_/start的“業(yè)務(wù)邏輯”就應(yīng)該和處理/upload_的不同。
在現(xiàn)在的實(shí)現(xiàn)下,路由過程會(huì)在路由模塊中“結(jié)束”,并且路由模塊并不是真正針對(duì)請(qǐng)求“采取行動(dòng)”的模塊,否則當(dāng)我們的應(yīng)用程序變得更為復(fù)雜時(shí),將無法很好地?cái)U(kuò)展。
我們暫時(shí)把作為路由目標(biāo)的函數(shù)稱為請(qǐng)求處理程序?,F(xiàn)在我們不要急著來開發(fā)路由模塊,因?yàn)槿绻?qǐng)求處理程序沒有就緒的話,再怎么完善路由模塊也沒有多大意義。
應(yīng)用程序需要新的部件,因此加入新的模塊 -- 已經(jīng)無需為此感到新奇了。我們來創(chuàng)建一個(gè)叫做requestHandlers的模塊,并對(duì)于每一個(gè)請(qǐng)求處理程序,添加一個(gè)占位用函數(shù),隨后將這些函數(shù)作為模塊的方法導(dǎo)出:
function start() {
? console.log("Request handler 'start' was called.");
}
function upload() {
? console.log("Request handler 'upload' was called.");
}
exports.start = start;
exports.upload = upload;
這樣我們就可以把請(qǐng)求處理程序和路由模塊連接起來,讓路由“有路可尋”。
在這里我們得做個(gè)決定:是將requestHandlers模塊硬編碼到路由里來使用,還是再添加一點(diǎn)依賴注入?雖然和其他模式一樣,依賴注入不應(yīng)該僅僅為使用而使用,但在現(xiàn)在這個(gè)情況下,使用依賴注入可以讓路由和請(qǐng)求處理程序之間的耦合更加松散,也因此能讓路由的重用性更高。
這意味著我們得將請(qǐng)求處理程序從服務(wù)器傳遞到路由中,但感覺上這么做更離譜了,我們得一路把這堆請(qǐng)求處理程序從我們的主文件傳遞到服務(wù)器中,再將之從服務(wù)器傳遞到路由。
那么我們要怎么傳遞這些請(qǐng)求處理程序呢?別看現(xiàn)在我們只有2個(gè)處理程序,在一個(gè)真實(shí)的應(yīng)用中,請(qǐng)求處理程序的數(shù)量會(huì)不斷增加,我們當(dāng)然不想每次有一個(gè)新的URL或請(qǐng)求處理程序時(shí),都要為了在路由里完成請(qǐng)求到處理程序的映射而反復(fù)折騰。除此之外,在路由里有一大堆_if request == x then call handler y_也使得系統(tǒng)丑陋不堪。
仔細(xì)想想,有一大堆東西,每個(gè)都要映射到一個(gè)字符串(就是請(qǐng)求的URL)上?似乎關(guān)聯(lián)數(shù)組(associative array)能完美勝任。
不過結(jié)果有點(diǎn)令人失望,JavaScript沒提供關(guān)聯(lián)數(shù)組 -- 也可以說它提供了?事實(shí)上,在JavaScript中,真正能提供此類功能的是它的對(duì)象。
在這方面,http://msdn.microsoft.com/en-us/magazine/cc163419.aspx有一個(gè)不錯(cuò)的介紹,我在此摘錄一段:
在C++或C#中,當(dāng)我們談到對(duì)象,指的是類或者結(jié)構(gòu)體的實(shí)例。對(duì)象根據(jù)他們實(shí)例化的模板(就是所謂的類),會(huì)擁有不同的屬性和方法。但在JavaScript里對(duì)象不是這個(gè)概念。在JavaScript中,對(duì)象就是一個(gè)鍵/值對(duì)的集合 -- 你可以把JavaScript的對(duì)象想象成一個(gè)鍵為字符串類型的字典。
但如果JavaScript的對(duì)象僅僅是鍵/值對(duì)的集合,它又怎么會(huì)擁有方法呢?好吧,這里的值可以是字符串、數(shù)字或者……函數(shù)!
好了,最后再回到代碼上來?,F(xiàn)在我們已經(jīng)確定將一系列請(qǐng)求處理程序通過一個(gè)對(duì)象來傳遞,并且需要使用松耦合的方式將這個(gè)對(duì)象注入到_route()_函數(shù)中。
我們先將這個(gè)對(duì)象引入到主文件_index.js_中:
var server = require("./server");
var router = require("./router");
var requestHandlers = require("./requestHandlers");
var handle = {}
handle["/"] = requestHandlers.start;
handle["/start"] = requestHandlers.start;
handle["/upload"] = requestHandlers.upload;
server.start(router.route, handle);
雖然_handle_并不僅僅是一個(gè)“東西”(一些請(qǐng)求處理程序的集合),我還是建議以一個(gè)動(dòng)詞作為其命名,這樣做可以讓我們?cè)诼酚芍惺褂酶鲿车谋磉_(dá)式,稍后會(huì)有說明。
正如所見,將不同的URL映射到相同的請(qǐng)求處理程序上是很容易的:只要在對(duì)象中添加一個(gè)鍵為_"/"_的屬性,對(duì)應(yīng)_requestHandlers.start即可,這樣我們就可以干凈簡(jiǎn)潔地配置/start和/_的請(qǐng)求都交由_start_這一處理程序處理。
在完成了對(duì)象的定義后,我們把它作為額外的參數(shù)傳遞給服務(wù)器,為此將_server.js_修改如下:
var http = require("http");
var url = require("url");
function start(route, handle) {?
function onRequest(request, response) {? ?
var pathname = url.parse(request.url).pathname;
? ? console.log("Request for " + pathname + " received.");
? ? route(handle, pathname);
? ? response.writeHead(200, {"Content-Type": "text/plain"});
? ? response.write("Hello World");
? ? response.end();? }
? http.createServer(onRequest).listen(8888);
? console.log("Server has started.");
}
exports.start = start;
這樣我們就在_start()_函數(shù)里添加了_handle_參數(shù),并且把handle對(duì)象作為第一個(gè)參數(shù)傳遞給了_route()_回調(diào)函數(shù)。
然后我們相應(yīng)地在_route.js_文件中修改_route()_函數(shù):
function route(handle, pathname) {
? console.log("About to route a request for " + pathname);?
if (typeof handle[pathname] === 'function') {
? ? handle[pathname]();? }
else {
? ? console.log("No request handler found for " + pathname);?
}
}
exports.route = route;
通過以上代碼,我們首先檢查給定的路徑對(duì)應(yīng)的請(qǐng)求處理程序是否存在,如果存在的話直接調(diào)用相應(yīng)的函數(shù)。我們可以用從關(guān)聯(lián)數(shù)組中獲取元素一樣的方式從傳遞的對(duì)象中獲取請(qǐng)求處理函數(shù),因此就有了簡(jiǎn)潔流暢的形如_handle[pathname]();_的表達(dá)式,這個(gè)感覺就像在前方中提到的那樣:“嗨,請(qǐng)幫我處理了這個(gè)路徑”。
有了這些,我們就把服務(wù)器、路由和請(qǐng)求處理程序在一起了?,F(xiàn)在我們啟動(dòng)應(yīng)用程序并在瀏覽器中訪問http://localhost:8888/start,以下日志可以說明系統(tǒng)調(diào)用了正確的請(qǐng)求處理程序:
Server has started.
Request for /start received.
About to route a request for /start
Request handler 'start' was called.
并且在瀏覽器中打開_http://localhost:8888/_可以看到這個(gè)請(qǐng)求同樣被_start_請(qǐng)求處理程序處理了:
Request for / received.
About to route a request for /
Request handler 'start' was called.
更多建議: