func-package

函数库, 面向复杂业务场景的 js 类库

koa-router 实现

前言

广义中间件,间接中间件方式

  • 不直接提供中间件
  • 通过间接方式提供了中间件,最常见的是间接中间件子中间件
  • 间接被 app.use() 加载
  • 其他方式接入Koa切面

这里 广义中间件,间接中间件方式实现 最代表性是第三方实现的 koa-router 中间件,这里基于第三方中间件 koa-router 用最简单的方式实现 koa-router 最简单功能。

实现步骤

  • 初始化路由实例
  • 注册路由请求信息缓存到实例中
    • 请求类型
    • 请求path
    • 对应的请求后操作
  • 注册的路由操作就是子中间件
  • 路由实例输出父中间件
    • 返回一个父中间件
    • 中间件里对每次请求进行遍历匹配缓存中注册的路由操作
    • 匹配上请求类型,路径就执行对应路由子中间件
  • app.use()路由实例返回的父中间件

实现源码

https://github.com/chenshenhai/koajs-design-note/tree/master/demo/chapter-06-01

## 安装依赖
npm i
## 执行 demo
npm run start
## 最后启动chrome浏览器访问
## http://127.0.0.1:3000/index
## http://127.0.0.1:3000/post
## http://127.0.0.1:3000/list
## http://127.0.0.1:3000/item

解读

const methods = [
'GET',
'PUT',
'PATCH',
'POST',
'DELETE'
];
class Layer {
constructor(path, methods, middleware, opts) {
this.path = path;
this.methods = methods;
this.middleware = middleware;
this.opts = opts;
}
}
class Router {
constructor(opts = {}) {
this.stack = [];
}
register(path, methods, middleware, opts) {
let route = new Layer(path, methods, middleware, opts);
this.stack.push(route);
return this;
}
routes() {
let stock = this.stack;
return async function(ctx, next) {
let currentPath = ctx.path;
let route;
for (let i = 0; i < stock.length; i++) {
let item = stock[i];
if (currentPath === item.path && item.methods.indexOf(ctx.method) >= 0) {
route = item.middleware;
break;
}
}
if (typeof route === 'function') {
route(ctx, next);
return;
}
await next();
};
}
}
methods.forEach(method => {
Router.prototype[method.toLowerCase()] = Router.prototype[method] = function(path, middleware) {
this.register(path, [method], middleware);
};
});
module.exports = Router;

使用

const Koa = require('koa');
const Router = require('./index');
const app = new Koa();
// 初始化路由实例
const router = new Router();
// 注册路由请求信息缓存到实例中
router.get('/index', async ctx => { ctx.body = 'index page'; });
router.get('/post', async ctx => { ctx.body = 'post page'; });
router.get('/list', async ctx => { ctx.body = 'list page'; });
router.get('/item', async ctx => { ctx.body = 'item page'; });
// 路由实例输出父中间件 router.routes()
app.use(router.routes());
app.use(async ctx => {
ctx.body = '404';
});
app.listen(3000);
console.log('listening on port 3000');

附录

参考