路由給真正的請(qǐng)求處理程序

2018-02-24 15:25 更新

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

掃描二維碼

下載編程獅App

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

編程獅公眾號(hào)