手把手教你如何编写Node.js HTTP服务器(基于connect框架)

发表时间: 2023-06-30 14:50


我上一篇文章通过阅读connect源码了解了它的大致原理,这篇文章做了进一步的细化和深入,可以一起动手,通过测试用例和最简单的框架代码来加深对connect的理解。在这个基础上,可以帮助我们更进一步了解express和koa等更为复杂的Node.js HTTP服务器框架。

上一篇文章的地址

如何手写一个Node.js HTTP服务器(参考connect)

参考Connect并通过TDD(测试驱动开发)的方式来手动编写一个简单的Node.js HTTP服务器。

test/app.listen.js

/* test/app.listen.js */var assert = require('assert')var myServer = require('..');var request = require('supertest');describe('app.listen()', function(){  it('should wrap in an http.Server', function(done){    var app = myServer();    app.use(function(req, res){      res.end();    });    var server = app.listen(0, function () {      assert.ok(server)      request(server)        .get('/')        .expect(200, function (err) {          server.close(function () {            done(err)          })        })    });  });});

这个测试用例的目的是验证服务器应用程序是否能够成功启动,并且能够处理发往根路径的 GET 请求,返回状态码为 200。

运行测试代码:npx mocha test/app.listen.js。

三处调用和相关API:

  1. 引用模块并实例化:
var myServer = require('..'); // 默认引用index.jsvar app = myServer();
  1. 声明中间件和相关处理逻辑:
app.use(function(req, res){  res.end();});
  1. 监听HTTP请求并处理回调逻辑:
    var server = app.listen(0, function () {      assert.ok(server)      request(server)        .get('/')        .expect(200, function (err) {          server.close(function () {            done(err)          })        })    });

编写对应的功能代码:

/*  index.js */const http = require('http')function createServer() {  let idx = 0  function app(req, res, next) {    app.handle(req, res, next);  }  app.handle = function(req, res, next) {    let idx = 0    const handle = this.stack[idx]    if (!handle) {      setImmediate(function() {        // done      });    }    handle(req, res, next)  }  app.use = function(handle) {    this.stack.push(handle)  }  app.listen = function() {    var server = http.createServer(this);    return server.listen.apply(server, arguments);  }  app.stack = []  return app}module.exports = createServer

运行测试:npx mocha test/app.listen.js。


来详细解释下这个最简单框架的代码:

// 引入内置的 http 模块const http = require('http');// 创建服务器函数 createServerfunction createServer() {  let idx = 0; // 用于追踪中间件函数数组的索引  // 创建 app 函数作为服务器处理请求的入口  function app(req, res, next) {    // 调用 app 的 handle 方法处理请求    app.handle(req, res, next);  }  // handle 方法用于处理请求的中间件函数  app.handle = function(req, res, next) {    let idx = 0; // 用于追踪中间件函数数组的索引    const handle = this.stack[idx]; // 获取当前索引处的中间件函数    // 如果没有中间件函数可执行,则使用 setImmediate 进行异步调用,表示请求处理完成    if (!handle) {      setImmediate(function() {        // 请求处理完成,没有中间件可执行      });    }    // 调用当前中间件函数处理请求,并传递 next 参数以便将控制权传递给下一个中间件函数    handle(req, res, next);  };  // use 方法用于向中间件函数数组中添加中间件函数  app.use = function(handle) {    this.stack.push(handle); // 将中间件函数添加到 stack 数组中  };  // listen 方法用于启动服务器  app.listen = function() {    // 创建基于 http 模块的服务器实例,并将 app 函数作为请求处理函数    var server = http.createServer(this);    // 使用 apply 方法调用 server.listen 方法,并传递 listen 方法的参数    return server.listen.apply(server, arguments);  };  app.stack = []; // 存储中间件函数的数组  return app; // 返回创建的 app 函数作为服务器实例}// 将 createServer 函数作为模块的导出内容module.exports = createServer;