最简Koa.js实现

前言

从上一章可以看到最简单的中间件式HTTP服务的实现,底层是基于回调嵌套去处理中间件队列。

  1. /**
  2. * 中间件总回调方法
  3. */
  4. callback() {
  5. let that = this;
  6. if (this.listeners('error').length === 0) {
  7. this.on('error', this.onerror);
  8. }
  9. const handleRequest = (req, res) => {
  10. let context = that.createContext(req, res);
  11. this.middleware.forEach((cb, idx) => {
  12. try {
  13. cb(context);
  14. } catch (err) {
  15. that.onerror(err);
  16. }
  17. if (idx + 1 >= this.middleware.length) {
  18. if (res && typeof res.end === 'function') {
  19. res.end();
  20. }
  21. }
  22. });
  23. };
  24. return handleRequest;
  25. }

但是中间件越多,回调嵌套越深,代码的可读性和可扩展性就很差,所以这时候把回调嵌套转化成 Promise + async/await ,这个时候就转变成最简单的Koa.js实现。

必要条件

  • 通过上下文赋值可代替 res.end()
  • 洋葱模型的中间件机制

源码实现

  • demo源码

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

  • 最简Koa.js 实现
  1. const http = require('http');
  2. const Emitter = require('events');
  3. // 注意:这里的compose是前几章的中间件引擎源码
  4. const compose = require('./../compose');
  5. /**
  6. * 通用上下文
  7. */
  8. const context = {
  9. _body: null,
  10. get body() {
  11. return this._body;
  12. },
  13. set body(val) {
  14. this._body = val;
  15. this.res.end(this._body);
  16. }
  17. };
  18. class SimpleKoa extends Emitter {
  19. constructor() {
  20. super();
  21. this.middleware = [];
  22. this.context = Object.create(context);
  23. }
  24. /**
  25. * 服务事件监听
  26. * @param {*} args
  27. */
  28. listen(...args) {
  29. const server = http.createServer(this.callback());
  30. return server.listen(...args);
  31. }
  32. /**
  33. * 注册使用中间件
  34. * @param {Function} fn
  35. */
  36. use(fn) {
  37. if (typeof fn === 'function') {
  38. this.middleware.push(fn);
  39. }
  40. }
  41. /**
  42. * 中间件总回调方法
  43. */
  44. callback() {
  45. if (this.listeners('error').length === 0) {
  46. this.on('error', this.onerror);
  47. }
  48. const handleRequest = (req, res) => {
  49. let context = this.createContext(req, res);
  50. let middleware = this.middleware;
  51. // 执行中间件
  52. compose(middleware)(context).catch(err => this.onerror(err))
  53. };
  54. return handleRequest;
  55. }
  56. /**
  57. * 异常处理监听
  58. * @param {EndOfStreamError} err
  59. */
  60. onerror(err) {
  61. console.log(err);
  62. }
  63. /**
  64. * 创建通用上下文
  65. * @param {Object} req
  66. * @param {Object} res
  67. */
  68. createContext(req, res) {
  69. let context = Object.create(this.context);
  70. context.req = req;
  71. context.res = res;
  72. return context;
  73. }
  74. }
  75. module.exports = SimpleKoa;
  • 执行例子
  1. const SimpleKoa = require('./index');
  2. const app = new SimpleKoa();
  3. const PORT = 3001;
  4. app.use(async ctx => {
  5. ctx.body = '<p>this is a body</p>';
  6. });
  7. app.listen(PORT, () => {
  8. console.log(`the web server is starting at port ${PORT}`);
  9. });