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

  1. ## 安装依赖
  2. npm i
  3. ## 执行 demo
  4. npm run start
  5. ## 最后启动chrome浏览器访问
  6. ## http://127.0.0.1:3000/index
  7. ## http://127.0.0.1:3000/post
  8. ## http://127.0.0.1:3000/list
  9. ## http://127.0.0.1:3000/item

解读

  1. const methods = [
  2. 'GET',
  3. 'PUT',
  4. 'PATCH',
  5. 'POST',
  6. 'DELETE'
  7. ];
  8. class Layer {
  9. constructor(path, methods, middleware, opts) {
  10. this.path = path;
  11. this.methods = methods;
  12. this.middleware = middleware;
  13. this.opts = opts;
  14. }
  15. }
  16. class Router {
  17. constructor(opts = {}) {
  18. this.stack = [];
  19. }
  20. register(path, methods, middleware, opts) {
  21. let route = new Layer(path, methods, middleware, opts);
  22. this.stack.push(route);
  23. return this;
  24. }
  25. routes() {
  26. let stock = this.stack;
  27. return async function(ctx, next) {
  28. let currentPath = ctx.path;
  29. let route;
  30. for (let i = 0; i < stock.length; i++) {
  31. let item = stock[i];
  32. if (currentPath === item.path && item.methods.indexOf(ctx.method) >= 0) {
  33. route = item.middleware;
  34. break;
  35. }
  36. }
  37. if (typeof route === 'function') {
  38. route(ctx, next);
  39. return;
  40. }
  41. await next();
  42. };
  43. }
  44. }
  45. methods.forEach(method => {
  46. Router.prototype[method.toLowerCase()] = Router.prototype[method] = function(path, middleware) {
  47. this.register(path, [method], middleware);
  48. };
  49. });
  50. module.exports = Router;

使用

  1. const Koa = require('koa');
  2. const Router = require('./index');
  3. const app = new Koa();
  4. // 初始化路由实例
  5. const router = new Router();
  6. // 注册路由请求信息缓存到实例中
  7. router.get('/index', async ctx => { ctx.body = 'index page'; });
  8. router.get('/post', async ctx => { ctx.body = 'post page'; });
  9. router.get('/list', async ctx => { ctx.body = 'list page'; });
  10. router.get('/item', async ctx => { ctx.body = 'item page'; });
  11. // 路由实例输出父中间件 router.routes()
  12. app.use(router.routes());
  13. app.use(async ctx => {
  14. ctx.body = '404';
  15. });
  16. app.listen(3000);
  17. console.log('listening on port 3000');

附录

参考