koa-router源碼分析

koa-router源碼地址是 koa-router
當前解讀版本爲7.2.1javascript

關係圖

代碼結構圖
代碼結構圖
執行流程圖
執行流程
關係對應圖
關係對應圖html

Router方法和屬性淺析

methods.forEach

註冊註冊路由的方法,結果就是Router的原型上面多了get,post,delete,del等註冊路由的方法
代碼使用的時候 r1.get就是這麼來的java

r1.get('/test1/:id', function (ctx, next) {
  console.log('test1 :1')
  next()
}, function (ctx, next) {
  console.log('test1:2')
})

Router.prototype.del

等同於 Router.prototype.deletenode

Router.prototype.use

註冊中間件,支持形式多種多樣
看着這麼多,其實就兩種中間件,git

  • 普通中間件
  • router.routes()返回的中間件

重點就是router.routes()返回的這種件,須要的前綴,參數驗中間件作一些處理github

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實例添加前綴,前綴能夠是包含參數的segmentfault

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),哪怕信息如出一轍app

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取值到路由參數了koa

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源碼解讀

Koa-router 優先級問題

Path-to-RegExp 使用

相關文章
相關標籤/搜索