前言

从上一章可以看到最简单的中间件式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. });