koa-router源碼分析


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

Koa-router 優先級問題

Path-to-RegExp 使用


免責聲明!

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



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