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


Express4.x API 譯文 系列文章

已經完成了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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM