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