路由給真正的請求處理程序

2018-02-24 15:25 更新

回到正題,現(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.
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號