Express4.x API (四):Router (譯)

Express4.x API 譯文 系列文章

已經完成了Express4.x API中的Requst和Response對象的文檔翻譯。簡單的總結,request對象即表示HTTP請求,包含了請求查詢字符串,參數,內容,HTTP頭等屬性;response對象則表示HTTP響應,即在受到請求時向客戶端發送的HTTP響應數據。Express則基於此提供給咱們一些方法,完成指定的請求和響應。html

技術庫更迭較快,很難使譯文和官方的API保持同步,咱們只有提高本身的英語能力才能更快的適應庫的更新迭代,閱讀到最新資料。
因此我這次翻譯的目的,一是熟悉express文檔,二是鍛鍊本身英語閱讀能力;正則表達式

原文地址:express.comexpress

Router

router對象是中間件和路由的隔離實例,你能夠把它看作一個僅能執行中間件和路由功能的mini-applaction,每個Express應用程序實例都有一個內置的路由器json

路由器的行爲相似於中間件自己,因此你能夠把他做爲一個參數傳遞給app.use()或者做爲參數傳遞給另外一個路由器的use()方法。Express top-level 對象有一個Router()建立一個新的路由器對象api

Properties

Router([options])

建立一個新的路由器對象數組

var router = express.Router([options]);

可選擇的options參數指定路由器的行爲app

Property Description Default
caseSensitive 是否啓用大小寫敏感 默認狀況下不敏感,以相同的方式對待"/Foo","/foo"
mergeParams 從父路由器保存req.params值,若是子父有衝突的參數名稱,以子路由參數優先 false
strict 啓用嚴格路由 默認狀況下是禁用的,"/foo"和"/foo/"是相同的

你能夠像應用程序那樣添加中間件和HTTP方法路由(例如get,put,post等等)函數

// 調用傳遞給次路由的任何請求
router.use(function(req,res,next){
    // 一些邏輯,和其餘中間件同樣
    next();
})

// 將會處理任何以/events結束的請求
router.get('/events',function(req,res,next){
    // ..
})

而後你能夠爲你特定的URL使用路由器,用這種方式把你的routes分爲文件甚至是mini-appspost

app.use('/calendar',router);

Methods

routers.all(path,[callback,...] callback)

這個方法就像router.METHHOD(),除了他匹配全部的HTTP方法學習

這個方法對於映射特定路徑前綴或任意匹配的"全局"邏輯很是有用。舉個栗子,若是你將如下路由置於全部路由的最前面,它要求從該點的全部路由都須要身份認證,並自動加載user。記住這些回調函數沒必要做爲終點,loadUser能夠執行任務,而後經過next()傳遞繼續匹配給後續的路由

router.all('*',requireAuthentication,loadUser);

等價於

router.all('*',requireAuthentication)
router.all('*',loadUser)

另外一個例子是white-listed"global"功能,這裏的例子很是的類似,可是它只限制路徑的前綴"/api"

router.all('/api/*',requireAuthentication);

router.METHOD(path,[callback,...] callback)

router.METHOD()方法在Express中提供路由功能,其中METHOD是HTTP方法之一,例如GET,POST,PUT等等,固然你能夠小寫。因此實際的方法是router.get()router.post()router.put()等等

router.get()函數將會自動的調用HTTP HEAD方法,除了router.head()router.get()以前要求沒有走這條路

你能夠提供多個回調,每一個回調都被平等對待,表現的就像中間件,除了這些回調函數能夠調用next(route)繞過其他路由回調。您可使用此機制在路由上執行預條件,而後在沒有理由繼續匹配路由的狀況下將控制傳遞給後續路由。

下面片斷展現了最簡單的路由定義,Express將字符串轉化爲正則表達式,在內部用於匹配傳入請求。執行這些匹配時不考慮查詢字符串,例如'GET'將匹配下面路由,像GET/?name='tobi'

router.get('/',function(req,res){
    res.send('hello world')
})

若是你有很是具體的約束條件,還可使用正則表達式。舉個栗子下面將會匹配"GET /commits/71dbb9c"以及 "GET /commits/71dbb9c..4c084f9".

router.get(/^\/commits\/(\w+)(?:\.\.(\w+))?$/,function(req,res){
    var from = req.params[0];
    var to = req.params[1] || 'HEAD';
    res.send('commit range' + form + '..' + to);
})

router.param(name, callback)

添加回調觸發到路由參數中,name是參數的名稱,callback是回調函數。雖然name在技術上是可選的,可是從Express v4.11.0沒有它是不推薦使用這種方法的(以下)

  • req,請求對象
  • res,響應對象
  • next,指示下一個中間件的功能
  • name參數的值
  • 參數的名稱

不像app.param(),router.param()不接受數組參數

舉個栗子,當:user在路由路徑中存在時,能夠將用戶加載映射爲自動提供req.user給這個路由,或者執行驗證的參數輸入

router.param('user',function(req,res,next,id){
    //嘗試從用戶模型獲取用戶詳細信息並將其附加到請求對象
    User.find(id,function(err,user){
        if(err){
            next(err);
        }else if(user){
            req.user = user;
            next();
        }else{
            next(new Error('fail to load user'))
        }
    }) 
})

該回調方法是在本地路由器上定義他們,它們不是由加載的應用程序或路由器繼承的。所以,定義在路由上的參數回調只有經過router定義的路由參數纔會觸發

一個回調參數將被稱爲一次請求響應週期,即便參數在多個路徑中匹配,以下面的栗子所示:

router.param('id',function(req,res,next,id){
    console.log('CALLED ONLY ONCE');
    next();
})

router.get('/user/:id',function(req,res,next){
    console.log('although this matchs ');
    next();
})

router.get('/user/:id',function(req,res){
    console.log('and this matchs too ');
    res.end();
})

將會依次打印:

CAALED ONLY ONCE
although this matchs
and this matchs too

如下部分描述router.param(callback)在v4.11.0將是過期的

router.param(name,callback)方法的行爲經過僅傳遞一個函數到router.param()將會徹底改變。此功能是如何實現router.param(name,callback)的習慣-它接受兩個參數,必須返回一箇中間件

函數返回的中間件決定了URL參數被捕獲時發生的行爲

在下面這個例子中,router.param(name,callback)簽名被修改成router.param(name, accessId)router.param()將會接受一個name和一個number而不是一個name和一個回調函數`

var express = require('express');
var app = express();
var router = express.Router();

// 定製 `router.param()`的功能
router.param(function(param,option){
    return function(req,res,next,val){
        if(val == option){
            next();
        }else{
            res.sendStatus(403);
        }
    }
})

// 使用定製的`router.param()`
router.param('id',1337);

// 觸發捕獲的路由
router.get('/user/:id',function(req,res){
    res.send('OK')
})

app.use(router);

app.listen(3000,function(){
    console.log('Ready');
})

在這個栗子中,router.param(name,callback)簽名是相同的,但不是一箇中間件回調,一個自定義檢查函數定義了驗證用戶ID

router.param(function(param,validator){
    return function(req,res,next,val){
        if(validator(val)){
            next();
        }else{
            res.sendStatus(403)
        }
    }
})

router.param('id',function(candidate){
    return !isNaN(parseFloat(candidate)) && isFinite(candidate)
})

router.route(path)

返回單個路由的實例,您可使用可選中間件來處理HTTP verbs,使用router.route()爲了不重複路由命名,從而鍵入錯誤。

在上面router.param()栗子的基礎上,下面的栗子展現瞭如何使用router.route()指定HTTP處理方法

var router = express.Router();

router.param('user_id',function(req,res,next,id){
    // 示例用戶,可能實際將從db等獲取
    req.user = {
        id:id,
        name:'TJ'
    };
    next();
})

router.route('/users/:user_id')
.all(function(req,res,next){
    // ..
    next()
})
.get(function(req,res,next){
    res.json(req.user)
})
.put(function(req,res){
    req.user.name = req.params.name;
    // 保存用戶等
    res.json(req.user) 
})
.post(function(req,res,next){
    next(new Error('not implemented'));
})
.delete(function(req,res,next){
    next(new Error('not implemented'))
});

這個方法再利用單一/users/:user_id路徑而且爲各類HTTP方法添加處理程序

router.use([path], [function, ...] function)

使用指定中間件函數或者函數,可選的參數是掛載路徑,默認是"/"

這個方法相似於app.use()。下面展現了一個簡單的示例和用例:

中間件就像是管道,請求在第一個中間件函數定義時開始,併爲它們"向下"匹配每一條路徑處理中間件堆棧處理。

var express = require('express');
var app = express();
var router = express.Router();

router.use(function(req,res){
    console.log('%s %s %s',req.method,req.url,req.path);
    next();
})

// 下面只有當路徑從掛載點開始時,纔會調用這個函數
router.use('/bar',function(req,res,next){
    // ..
    next();
})

// 老是調用
router.use(function(req,res,next){
    res.send('Hello world')
});

app.use('/foo',router);

app.listen(3000);

「掛載」路徑被剝離而且對中間件功能不可見。這個功能的主要做用是:無論它的"prefix前綴"路徑,安裝中間件功能可能沒有代碼的變化

爲了保證您使用router.use()定義的中間件的重要性。他們按順序調用,所以順序定義中間件優先級。舉個栗子:一般logger是您將使用的第一個中間件,所以每一個請求都會被記錄

var logger = require('morgan');

router.user(logger());
router.use(express.static(__dirname+'/public'));
router.use(function(req,res){
    res.send('hello')
})

如今假設您忽略了對靜態文件的日誌請求,可是在logger()以後要繼續記錄路由和中間件定義。你只需簡單的移動express.static()到頂部,在添加日誌中間件以前便可。

router.use(express.static(__dirname + '/public'));
router.use(logger());
router.use(function(req,res){
    res.send('Hello')
})

另外一個例子是從多個目錄中服務文件,給予"/public"優先

app.use(express.static(__dirname + '/public'));
app.use(express.static(__dirname + '/files'));
app.use(express.static(__dirname + '/uploads'));

router.use()方法也支持命名參數,這樣,其餘路由器的掛載點能夠經過使用命名參數預加載來獲益。

NOTE:雖然這些中間件功能是經過特定路由器添加的,當他們運行時由他們鏈接到的路徑來定義(而不是路由)。所以,若是路由器的路由匹配,則經過一個路由器添加的中間件能夠運行其餘路由器。舉個栗子,下面顯示安裝在同一路徑上的兩個不一樣的路由器:

var autoRouter = express.Router();
var openRouter = express.Router();

autoRouter.use(require('./authenticate').basic(usersdb));

autoRouter.get('/:user_id/edit',function(req,res,next){
    // .. 編輯用戶界面 .. 
})
openRouter.get('/',function(req,res,next){
    // .. 用戶列表 ..
})
openRouter.get('/:user_id',function(req,res,next){
    // .. 查看用戶 .. 
})

app.use('/users',authRouter);
app.use('/users',openRouter);

儘管authenticate中間件是經過autoRouter路由加入的,可是它也將運行在openRouter定義的路由上,由於兩個路由器都掛載在/users。爲了不這種行爲發生,爲每一個路由器使用不一樣的路徑。

寫在後面

Express文檔中Router部分就完成了,本人學識有限,不免有所紕漏或者理解不當之處,翻譯僅僅是方便我的學習交流使用,無其餘用意,原文地址:expressjs.com

相關文章
相關標籤/搜索