Express4.x API 譯文 系列文章
- Express4.x API (一):application (譯) -- 完成
- Express4.x API (二):request (譯) -- 完成
- Express4.x API (三):Response (譯) -- 完成
- Express4.x API (四):router (譯) -- 完成
已經完成了Express4.x API中的Requst和Response對象的文檔翻譯。簡單的總結,request對象即表示HTTP請求,包含了請求查詢字符串,參數,內容,HTTP頭等屬性;response對象則表示HTTP響應,即在受到請求時向客戶端發送的HTTP響應數據。Express則基於此提供給我們一些方法,完成指定的請求和響應。
技術庫更迭較快,很難使譯文和官方的API保持同步,我們只有提升自己的英語能力才能更快的適應庫的更新迭代,閱讀到最新資料。
所以我此次翻譯的目的,一是熟悉express文檔,二是鍛煉自己英語閱讀能力;
原文地址:express.com
Router
router
對象是中間件和路由的隔離實例,你可以把它看做一個僅能執行中間件和路由功能的mini-applaction
,每一個Express應用程序實例都有一個內置的路由器
路由器的行為類似於中間件本身,所以你可以把他作為一個參數傳遞給app.use()
或者作為參數傳遞給另一個路由器的use()
方法。Express top-level
對象有一個Router()創建一個新的路由器對象
Properties
Router([options])
創建一個新的路由器對象
var router = express.Router([options]);
可選擇的options參數指定路由器的行為
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-apps
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