koa框架會用也會寫—(koa-router)

夢想攻城獅發表於2018-09-28

Koa中常用的中介軟體:

  • koa-session:讓無狀態的http擁有狀態,基於cookie實現的後臺儲存資訊的session
  • koa-mysql:封裝了需要用到的SQL語句
  • koa-mysql-session:當不想讓session儲存到記憶體,而想讓session儲存到mysql資料庫中時使用
  • koa-router:後臺會接受到各種請求的url,路由會根據不同的url來使用不同的處理邏輯。
  • koa-view:請求html頁面時,後臺會用模板引擎渲染資料到模板上,然後返回給後臺
  • koa-static:請求img、js、css等檔案時,不需要其他邏輯,只需要讀取檔案
  • koa-better-body:post上傳檔案時,解析請求體

koa系列文章:

koa-router的使用

var Koa = require('koa');
var Router = require('koa-router');
var app = new Koa();
var router = new Router();
router.get('/home',(ctx,next)=>{ 
  ctx.body = 'home'
  next();
});
router.get('/user', (ctx, next) => {
  ctx.body = 'user';
  next();
});
app.use(router.routes()).use(router.allowedMethods());
複製程式碼

koa-router的奧祕

假如沒有koa-router

var Koa = require('koa');
var Router = require('koa-router');
var app = new Koa();
var router = new Router();
//將路由的處理交給中介軟體
app.use((ctx, next) => {
    if (ctx.path === '/' && ctx.method === 'GET') {
        ctx.body = '首頁'
   } else {
        next();
   }
})
app.use((ctx, next) => {
   if (ctx.path === '/user' && ctx.method === 'GET') {
        ctx.body = '使用者'
   } else {
        next();
  }
});
複製程式碼

從上面可以知道,如果沒有koa-router,其實每個路由使用的koa註冊中介軟體的形式來進行處理的,這樣不利於鬆耦合和模組化,所以將所有路由的處理邏輯抽離出來組合成一個大的中介軟體koa-router來處理,最後將大的中介軟體註冊到koa上,如果關於koa中介軟體原理還不瞭解,可以參考另一篇文章koa框架會用也會寫—(koa的實現)

koa-router的原理

既然koa-router也是大的中介軟體,裡面擁有許多小的中介軟體,那麼裡面必然也需要用到洋蔥模型,洋蔥模型的特點:

  • middles:存放中介軟體的容器,用來存放註冊的中介軟體
  • get(path,fn):用來註冊中介軟體,往middles中存放,由於是路由中介軟體這裡多了一個引數path
  • compose():用來組合中介軟體,讓路由中介軟體按順序執行
  • routes():用來將koa-router中介軟體註冊到app的中介軟體上,主要作用是呼叫路由中介軟體匹配請求的路徑ctx.path

如果對於中介軟體和洋蔥模型有疑問的,可以參考koa框架會用也會寫—(koa的實現)

middles:存放中介軟體的容器,用來存放註冊的中介軟體

class Router {
    constructor(){
        this.middles=[];
    }
}
module.exports = Router
複製程式碼

get(path,fn):用來註冊中介軟體,往middles中存放,由於是路由中介軟體這裡多了一個引數path

class Router {
    constructor(){
        this.middles=[];
    }
    get(path,fn){//set,post等同理
        let layer = {
            path,
            fn,
            method
        }
        //處理類似/article/:id的路由
        if(path.includes(':')){ 
            let params  = [];
            let reg = path.replace(/:([^\/]*)/g,function () {
                params.push(arguments[1]);  //params = [id]
                return '([^\/]*)'   //返會字串/article/([^\/]*)
            });
            //將返回的字串變成正則,後面解析路由是會用到
            layer.reg = new RegExp(reg);//返回/\/article\/([^\/]*)/
            layer.params = params;
      }
        this.middles.push(layer);
    }
}
module.exports = Router
複製程式碼

compose():用來組合中介軟體,讓路由中介軟體按順序執行

class Router {
    constructor(){
        this.middles=[];
    }
    get(path,fn){//set,post等同理
        let layer = {
            path,
            fn,
            method
        }
        //處理類似/article/:id的路由
        if(path.includes(':')){ 
            let params  = [];
            let reg = path.replace(/:([^\/]*)/g,function () {
                params.push(arguments[1]);  //params = [id]
                return '([^\/]*)'   //返會字串/article/([^\/]*)
            });
            //將返回的字串變成正則,後面解析路由是會用到
            layer.reg = new RegExp(reg);//返回/\/article\/([^\/]*)/
            layer.params = params;
      }
        this.middles.push(layer);
    }
    compose(lasts,next,ctx){//lasts為匹配的路由集合 
        dispatch(index){
            if(index === lasts.length) return next();
            let route = lasts[index];
            //將路徑引數都取出來exp:id的值
            let params = route.params;
            let [, ...args] = pathname.match(route.reg);
            ctx.request.params = params.reduce((memo,key,index)=>(memo[key] = args[index], memo), {});
            //執行路由邏輯,next賦值為下一個路由邏輯
            route.fn(ctx,()=>{  
                dispatch(index+1);
            })
        }
        dispatch(0)
    }
}
module.exports = Router
複製程式碼

routes():用來將koa-router中介軟體註冊到app的中介軟體上,主要作用是呼叫路由中介軟體匹配請求的路徑ctx.path

class Router {
    constructor(){
        this.middles=[];
    }
    get(path,fn){//set,post等同理
        let layer = {
            path,
            fn,
            method
        }
        //處理類似/article/:id的路由
        if(path.includes(':')){ 
            let params  = [];
            let reg = path.replace(/:([^\/]*)/g,function () {
                params.push(arguments[1]);  //params = [id]
                return '([^\/]*)'   //返會字串/article/([^\/]*)
            });
            //將返回的字串變成正則,後面解析路由是會用到
            layer.reg = new RegExp(reg);//返回/\/article\/([^\/]*)/
            layer.params = params;
      }
        this.middles.push(layer);
    }
    compose(lasts,next,ctx){//lasts為匹配的路由集合 
        dispatch(index){
            if(index === lasts.length) return next();
            let route = lasts[index];
            route.fn(ctx,()=>{
                dispatch(index+1);
            })
        }
        dispatch(0)
    }
    routes() {
    // ctx上下文next指的是koa中的next方法
    return async (ctx, next) => {
      let pathname = ctx.path;  //請求的路徑
      let lasts = this.middles.filter(item => {
            if (route.reg) {     // 說明當前路由是一個路徑引數
                if (method === route.method && route.reg.test(pathname)) {
                    return true //路徑引數匹配到了,新增進路由組 
                }
            }
            if ((method === route.method || route.method === 'all') && (route.p === pathname || route.p === '*')) {
                return true //路徑是'/'或者'all',新增進路由組 
            }
            return false;
      });
      this.compose(lasts, next, ctx);
    }
  }
}
module.exports = Router
複製程式碼

關於其他

上面的router是簡化版的koa-router,它只實現了koa-router中的一級路由,但是卻是能說明koa-router的主要思想,koa-router中新增了use來註冊二級路由,同時新增了很多包括重定向等其他邏輯處理

結語

koa-router中介軟體的原理基本就介紹完了,後面一起學習kao的其他中介軟體:

相關文章