koa-router源碼地址是 koa-router
當前解讀版本為7.2.1
關系圖
代碼結構圖
執行流程圖
關系對應圖
Router方法和屬性淺析
methods.forEach
注冊注冊路由的方法,結果就是Router的原型上面多了get,post,delete,del等注冊路由的方法
代碼使用的時候 r1.get就是這么來的
r1.get('/test1/:id', function (ctx, next) {
console.log('test1 :1')
next()
}, function (ctx, next) {
console.log('test1:2')
})
Router.prototype.del
等同於 Router.prototype.delete
Router.prototype.use
注冊中間件,支持形式多種多樣
看着這么多,其實就兩種中間件,
- 普通中間件
- router.routes()返回的中間件
重點就是router.routes()返回的這種件,需要的前綴,參數驗中間件做一些處理
router.use(function (ctx, next) {
ctx.foo = 'baz';
return next();
});
router.use('/foo/bar', function (ctx, next) {
ctx.foo = 'foo';
return next();
});
router.use('/foo', subrouter.routes());
router.use(['/foo', '/bar'], function (ctx, next) {
ctx.foo = 'foo';
ctx.bar = 'bar';
return next();
});
parentRouter.use('/parent-route', function (ctx, next) {
ctx.n = ctx.n ? (ctx.n + 1) : 1;
return next();
}, nestedRouter.routes());
Router.prototype.prefix
給router實例添加前綴,前綴可以是包含參數的
router.prefix('/things/:thing_id')
Router.prototype.routes = Router.prototype.middleware
返回中間件,
中間執行的時候,會根據path獲取滿足匹配條件的路由(Layer),然后根據每個Layer生成一個解析參數值的中間,這就是為什么我們在ctx.params能得到參數值
最核心的代碼如下
layerChain = matchedLayers.reduce(function(memo, layer) {
memo.push(function(ctx, next) {
ctx.captures = layer.captures(path, ctx.captures);
ctx.params = layer.params(path, ctx.captures, ctx.params);
return next();
});
return memo.concat(layer.stack);
}, []);
return compose(layerChain)(ctx, next);
matchedLayers是匹配的Layer或者說一條路由信息,同一個路徑同樣的方法也是會生成兩條記錄的,如下同樣的注冊,會生成兩個不同路由(Layer),哪怕信息一模一樣
r1.get('/test1', function (ctx, next) {
console.log('test1 :1')
next()
})
r1.get('/test1', function (ctx, next) {
console.log('test1 :2')
next()
})
matchedLayers.reduce沒執行一次,是生成兩個中間件,
一個是參數解析的中間件,這就是為什么你可以通過ctx.params取值到路由參數了
function(ctx, next) {
ctx.captures = layer.captures(path, ctx.captures);
ctx.params = layer.params(path, ctx.captures, ctx.params);
return next();
}
另外一個才是實際路由匹配的執行方法,上面的demo就是
function (ctx, next) {
console.log('test1 :1')
next()
}
Router.prototype.allowedMethods
此方法執行很靠后,在他后面注冊的中間件執行完畢后才執行
生成一個中間件,作用是定義路由沒匹配到,方法未允許,方法未實現等的返回信息
app.use(router.routes());
app.use(router.allowedMethods({
throw: true,
notImplemented: () => new Boom.notImplemented(),
methodNotAllowed: () => new Boom.methodNotAllowed()
}));
Router.prototype.all
注冊一個路由,允許所有的get,post等方法訪問
Router.prototype.redirect
跳轉,原理就是注冊了一個路由,用ctx.redirect來實現跳轉
router.redirect('/login', 'sign-in');
等同於
router.all('/login', function (ctx) {
ctx.redirect('/sign-in');
ctx.status = 301;
});
Router.prototype.register
核心方法之一,注冊中間件
Router.prototype.all,methods.forEach等底層都是調用這個家伙實現的
Router.prototype.use也依據情況會調用
Router.prototype.route
查找具名的route(Layer)
Router.prototype.url
生成url,可以傳參
router.get('user', '/users/:id', function (ctx, next) {
// ...
});
router.url('user', 3);
// => "/users/3"
router.url('user', { id: 3 });
// => "/users/3"
router.use(function (ctx, next) {
// redirect to named route
ctx.redirect(ctx.router.url('sign-in'));
})
Router.prototype.match
獲得匹配的路由(Layer),以path和method來過濾的
router.routes返回的中間件底層就是通過他來確認請求應該進入哪些路由的
Router.prototype.param
添加參數驗證中間件,這個需要結合Layer.prototype.param 一起來理解
Router.prototype.param = function (param, middleware) {
this.params[param] = middleware;
this.stack.forEach(function (route) {
route.param(param, middleware);
});
return this;
};
Layer.prototype.param = function (param, fn) {
var stack = this.stack;
var params = this.paramNames;
// 構建參數驗證中間件
var middleware = function (ctx, next) {
return fn.call(this, ctx.params[param], ctx, next);
};
middleware.param = param;
var names = params.map(function (p) {
return p.name;
});
var x = names.indexOf(param);
if (x > -1) {
// iterate through the stack, to figure out where to place the handler fn
stack.some(function (fn, i) {
// param handlers are always first, so when we find an fn w/o a param property, stop here
// if the param handler at this part of the stack comes after the one we are adding, stop here
// fn.param 作為判斷是不是參數驗證中間件的標志
// 如果不是參數驗證中間件,或者參數驗證中間件需要驗證的參數在我之后,插入參數驗證中間件
// 比如說path是這樣的 /user/:id/posts/:postid, 那么id參數驗證中間件應該在postid參數之前
// 簡單說,確保參數按照順序被驗證
if (!fn.param || names.indexOf(fn.param) > x) {
// inject this param handler right before the current item
stack.splice(i, 0, middleware);
return true; // then break the loop
}
});
}
return this;
};
Layer方法和屬性淺析
Layer.prototype.match
path是否匹配路由
Layer.prototype.params
獲得路由參數鍵值對
Layer.prototype.captures
獲得路由參數的值
Layer.prototype.url
用參數構建URL,params參數可視是對象也可是數組
Layer.prototype.param
添加參數驗證中間件
Layer.prototype.setPrefix
設置前綴
safeDecodeURIComponent
decodeURIComponent錯誤時返回原值
更多細節請直接看帶備注的源碼吧,寫東西真累啊!
Router
Layer
koa-router 源碼解析 - segmentfault
解析Koa-Router,邁入Web次時代第一步(上)
Koa-Router 源碼解析下 -CNode
koa-router源碼解讀