前言

用过Express.jsKoa.js的人会发现使用方式很类似,也是基于中间件的理念去实现Web服务。
直接以Express.js回调式的中间件服务比较容易理解。再基于回调式的中间件服务接入Koa.js的中间件引擎去处理回调嵌套的处理。
这一章主要以原生的Node.js实现纯回调的中间件HTTP服务。

必要条件

  • 内置中间件队列
  • 中间件遍历机制
  • 异常处理机制

    最简实现

  • demo源码

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

  • 服务类封装

    1. const http = require('http');
    2. const Emitter = require('events');
    3. class WebServer extends Emitter {
    4. constructor() {
    5. super();
    6. this.middleware = [];
    7. this.context = Object.create({});
    8. }
    9. /**
    10. * 服务事件监听
    11. * @param {*} args
    12. */
    13. listen(...args) {
    14. const server = http.createServer(this.callback());
    15. return server.listen(...args);
    16. }
    17. /**
    18. * 注册使用中间件
    19. * @param {Function} fn
    20. */
    21. use(fn) {
    22. if (typeof fn === 'function') {
    23. this.middleware.push(fn);
    24. }
    25. }
    26. /**
    27. * 中间件总回调方法
    28. */
    29. callback() {
    30. let that = this;
    31. if (this.listeners('error').length === 0) {
    32. this.on('error', this.onerror);
    33. }
    34. const handleRequest = (req, res) => {
    35. let context = that.createContext(req, res);
    36. this.middleware.forEach((cb, idx) => {
    37. try {
    38. cb(context);
    39. } catch (err) {
    40. that.onerror(err);
    41. }
    42. if (idx + 1 >= this.middleware.length) {
    43. if (res && typeof res.end === 'function') {
    44. res.end();
    45. }
    46. }
    47. });
    48. };
    49. return handleRequest;
    50. }
    51. /**
    52. * 异常处理监听
    53. * @param {EndOfStreamError} err
    54. */
    55. onerror(err) {
    56. console.log(err);
    57. }
    58. /**
    59. * 创建通用上下文
    60. * @param {Object} req
    61. * @param {Object} res
    62. */
    63. createContext(req, res) {
    64. let context = Object.create(this.context);
    65. context.req = req;
    66. context.res = res;
    67. return context;
    68. }
    69. }
    70. module.exports = WebServer;
  • 服务使用

    1. const WebServer = require('./index');
    2. const app = new WebServer();
    3. const PORT = 3001;
    4. app.use(ctx => {
    5. ctx.res.write('<p>line 1</p>');
    6. });
    7. app.use(ctx => {
    8. ctx.res.write('<p>line 2</p>');
    9. });
    10. app.use(ctx => {
    11. ctx.res.write('<p>line 3</p>');
    12. });
    13. app.listen(PORT, () => {
    14. console.log(`the web server is starting at port ${PORT}`);
    15. });