手把手教你如何编写Node.js HTTP服务器(基于connect框架)
发表时间: 2023-06-30 14:50
我上一篇文章通过阅读connect源码了解了它的大致原理,这篇文章做了进一步的细化和深入,可以一起动手,通过测试用例和最简单的框架代码来加深对connect的理解。在这个基础上,可以帮助我们更进一步了解express和koa等更为复杂的Node.js HTTP服务器框架。
上一篇文章的地址
参考Connect并通过TDD(测试驱动开发)的方式来手动编写一个简单的Node.js HTTP服务器。
/* 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:
var myServer = require('..'); // 默认引用index.jsvar 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) }) }) });
编写对应的功能代码:
/* 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;