1. 程式人生 > >[轉] NodeJS框架express的途徑對映(路由)功能及控制

[轉] NodeJS框架express的途徑對映(路由)功能及控制

NodeJS框架express的路徑對映(路由)功能及控制

 

我們知道Express是一個基於NodeJS的非常優秀的服務端開發框架,本篇CSSer將提供express框架的route和route control章節,route實現了客戶端請求的URL的路徑對映功能,暫且譯為路由或URL對映吧。如果你還是不太理解,相信看完本篇文章將會有些收穫的。

路由(URL對映)

Express利用HTTP動作提供了有意義並富有表現力的URL對映API,例如我們可能想讓使用者帳號的URL看起來像“/user/12”的樣子,下面的例子就能實現這樣的路由,其中與佔位識別符號(本例為:id)相關的值可以被req.params獲取到。

app.get('/user/:id', function(req, res){
    res.send('user ' + req.params.id); });

上例中當我們訪問/user/12時返回“user 12”,CSSer注:app.get相當於在伺服器註冊了一個監聽get請求事件的偵聽器,當請求的URL滿足第一個引數時,執行後面的回撥函式,該過程是非同步的。

路由是一個可以被內部編譯成正則表示式的簡單字串,比如當/user/:id被編譯後,被內部編譯後的正則表示式字串看起來會是下面的樣子(簡化後):

\/user\/([^\/]+)\/?

要實現複雜點的,我們可以傳入正則表示式直接量,因為正則捕獲組是匿名的因此我們可以通過req.params進行訪問,第一個捕獲組應該是req.params[0],第二個應該是req.params[1],以此類推。

app.get(/^\/users?(?:\/(\d+)(?:\.\.(\d+))?)?/, function(req, res){
    res.send(req.params);
});

通過Linux的curl命令來測試我們定義的路由:

$ curl http://cssercom:3000/user
[null,null]
$ curl http://cssercom:3000/users
[null,null] $ curl http://cssercom:3000/users/1 ["1",null] $ curl http://cssercom:3000/users/1..15 ["1","15"]

下面是一些路由例子,以及與之相匹配的關聯路徑:

"/user/:id"
/user/12

"/users/:id?"
/users/5
/users

"/files/*"
/files/jquery.js
/files/javascripts/jquery.js

"/file/*.*"
/files/jquery.js
/files/javascripts/jquery.js

"/user/:id/:operation?" /user/1 /user/1/edit "/products.:format" /products.json /products.xml "/products.:format?" /products.json /products.xml /products "/user/:id.:format?" /user/12 /user/12.json

另外,我們可以通過POST方式提交json資料,然後利用bodyParser中介軟體解析json請求體並把json資料返回給客戶端:

var express = require('express')
  , app = express.createServer();

app.use(express.bodyParser());

app.post('/', function(req, res){   res.send(req.body); }); app.listen(3000);

通常我們所使用的佔位符(比如/user/:id)都沒有任何限制,即使用者可以傳入各種各樣資料型別的id值,如果我們希望限制使用者id為數字,可以這樣寫“/user/:id(\d+)”,這樣就能保證只有該佔位符資料型別為數值型別才會進行路由的相關處理。

路由控制

一個應用中可以定義多個路由,我們可以控制以令其轉向下一個路由,Express提供了第三個引數即next()函式。當一個模式不被匹配時,控制將被轉回Connect(Express基於Connect模組),同時中介軟體會繼續按照它們在use()中增加的順序來執行。當多個定義的路由都可能匹配同一個URL時也是如此,除非某個路由並不呼叫next()且已將響應輸出到客戶端,否則它們也將按順序執行。

app.get('/users/:id?', function(req, res, next){
    var id = req.params.id;     if (id) {         // 一回注:如果在這裡就將響應內容輸出給客戶端,那麼後續的URL對映將不會被呼叫     } else {         next(); // 將控制轉向下一個符合URL的路由     } }); app.get('/users', function(req, res){     // do something else });

app.all()方法可以對所有HTTP動作應用單一呼叫入口,這在有些情況下很有用。下面我們使用該功能來從我們的模擬資料庫中載入一個使用者,並把它分配給req.user。

var express = require('express')
  , app = express.createServer();

var users = [{ name: 'www.csser.com' }]; app.all('/user/:id/:op?', function(req, res, next){   req.user = users[req.params.id];   if (req.user) {     next();   } else {     next(new Error('cannot find user ' + req.params.id));   } }); app.get('/user/:id', function(req, res){   res.send('viewing ' + req.user.name); }); app.get('/user/:id/edit', function(req, res){   res.send('editing ' + req.user.name); }); app.put('/user/:id', function(req, res){   res.send('updating ' + req.user.name); }); app.get('*', function(req, res){   res.send('what???', 404); }); app.listen(3000);

路由引數預處理

本節為後補(2011-4-16)。

路由引數預處理通過隱式的資料處理,可以大幅提高應用程式碼的可讀性和請求URL的驗證。假如你經常性的從幾個路由獲取通用資料,如通過/user/:id載入使用者資訊,通常我們可能會這樣做:

app.get('/user/:userId', function(req, res, next){
  User.get(req.params.userId, function(err, user){     if (err) return next(err);     res.send('user ' + user.name);   }); });

利用預處理後引數可以被對映到回撥函式,從而可以提供諸如驗證、強制性改變值,甚至從資料庫中載入資料等功能。下面我們將呼叫app.param()並傳入我們希望對映到某個中介軟體的引數,可以看到我們接收了包含佔位符(:userId)值的id引數。在這裡可以與平常一樣進行使用者資料載入以及錯誤處理,並能簡單的通過呼叫next()將控制權轉向下一個預處理或路由(路徑控制)。

app.param('userId', function(req, res, next, id){
  User.get(id, function(err, user){     if (err) return next(err);     if (!user) return next(new Error('failed to find user'));     req.user = user;     next();   }); });

這樣做,不僅向上面提到的可以大幅提高路由的可讀性,還能在整個應用中共享該部分的邏輯實現,達到複用目的。

app.get('/user/:userId', function(req, res){
  res.send('CSSer使用者為 ' + req.user.name); });

對於簡單的情況如路由佔位符驗證和強迫改變值,只需要傳入1個引數(支援1個引數),期間丟擲的異常將自動傳入next(err)。

app.param('number', function(n){ return parseInt(n, 10); });

也可以同時將回調函式應用到多個佔位符,比如路由/commits/:from-:to來說,:from和:to都是數值型別,我們可以將它們定義為陣列:

app.param(['from', 'to'], function(n){ return parseInt(n, 10); });

結語

通過本文的學習,我們應該有些感覺了,NodeJS不僅僅可以實現我們產品的服務端邏輯,同時我們還可以利用Javascript做伺服器程式設計,注意是伺服器,也就是說,我們可以利用Javascript來定製以往只能在apache中才可以做到的功能。NodeJS還需要rewrite嗎?路徑對映更簡單更強大,還要rewrite幹嘛用?