来详细讲解一下“JavaScript 实现类似Express的中间件系统(实例详解)”的攻略。
简介
中间件是实现 Express 等框架功能的核心。本文主要讲解如何通过 JavaScript 实现一个类似 Express 的中间件系统。
实现过程
1. 实现基本的 Application
类
首先,我们需要创建一个 Application
类,表示整个应用。这个类需要实现以下功能:
- 注册路由和中间件
- 解析请求体和响应头
- 处理错误
class Application {
constructor() {
// 路由和中间件列表
this.routes = {
all: [],
get: [],
post: []
};
}
// 注册路由和中间件
use(path, middleware) {
let info = {};
if (typeof path === "string") {
info.path = path;
info.middleware = middleware;
} else {
info.path = "/";
info.middleware = path;
}
this.routes.all.push(info);
}
// 处理请求
handle(req, res) {
let self = this;
let idx = 0;
let stack = self.routes.all;
function next() {
let middleware = stack[idx];
if (!middleware) return;
if (middleware.path === "/" || middleware.path === req.path || middleware.path === req.url) {
middleware.middleware(req, res, next);
} else {
next();
}
}
next();
}
// 监听端口
listen(...args) {
let server = http.createServer(this.handle.bind(this));
server.listen(...args);
}
}
2. 实现中间件
中间件的实现方式很简单,它们是一个函数,接收 req
、res
和 next
三个参数,最后必须调用 next
,否则后续的中间件将无法执行。
3. 注册路由和中间件
我们可以通过 use
方法注册路由和中间件,这个方法接收两个参数,第一个参数表示路径,可以是字符串或正则表达式;第二个参数表示中间件。
4. 处理请求
在处理请求时,我们需要遍历所有注册的中间件和路由,直到找到匹配的路由或中间件,然后依次执行它们。
class Application {
// 省略其他代码
handle(req, res) {
let self = this;
let idx = 0;
// 获取指定方法类型的路由和中间件
let method = req.method.toLowerCase();
let stack = self.routes.all.concat(self.routes[method]);
function next() {
let middleware = stack[idx];
if (!middleware) return;
// 匹配路径
if (middleware.path === "/" || middleware.path === req.path || middleware.path === req.url) {
middleware.middleware(req, res, next);
} else {
idx++;
next();
}
}
next();
}
}
5. 解析请求体和响应头
为了方便使用,在解析请求体和响应头时,我们可以定义一些常见的中间件,如下:
// 解析 JSON 格式请求体
const bodyParserJSON = function (req, res, next) {
let data = "";
req.on("data", function (chunk) {
data += chunk;
});
req.on("end", function () {
try {
req.body = JSON.parse(data);
next();
} catch (err) {
res.statusCode = 400;
return res.end("bad request.");
}
});
};
// 解析表单请求体
const bodyParserUrlencoded = function (req, res, next) {
let data = "";
req.on("data", function (chunk) {
data += chunk;
});
req.on("end", function () {
req.body = qs.parse(data);
next();
});
};
// 设置响应头
const setContentType = function (req, res, next) {
res.setHeader("Content-Type", "text/html;charset=utf-8");
next();
};
6. 错误处理
最后,在处理请求过程中,难免会出现一些错误。我们可以通过全局中间件来捕获错误并处理,如下:
// 全局错误处理中间件
const errorHandler = function (err, req, res, next) {
console.error(err);
res.statusCode = 500;
res.end("Internal Server Error");
};
app.use(setContentType);
app.use(bodyParserJSON);
app.use(bodyParserUrlencoded);
app.use(errorHandler);
示例说明
接下来,我们使用上述代码创建一个简单的 HTTP 服务器,并实现以下两个接口:
- GET /,返回 “Hello World!”
- POST /api/user,返回请求体中的
name
字段
const http = require("http");
const { parse } = require("url");
const qs = require("qs");
class Application {
constructor() {
// 路由和中间件列表
this.routes = {
all: [],
get: [],
post: []
};
}
// 注册路由和中间件
use(path, middleware) {
let info = {};
if (typeof path === "string") {
info.path = path;
info.middleware = middleware;
} else {
info.path = "/";
info.middleware = path;
}
this.routes.all.push(info);
}
// 处理请求
handle(req, res) {
let self = this;
let idx = 0;
// 获取指定方法类型的路由和中间件
let method = req.method.toLowerCase();
let stack = self.routes.all.concat(self.routes[method]);
function next() {
let middleware = stack[idx];
if (!middleware) return;
// 匹配路径
if (middleware.path === "/" || middleware.path === req.path || middleware.path === req.url) {
middleware.middleware(req, res, next);
} else {
idx++;
next();
}
}
next();
}
// 监听端口
listen(...args) {
let server = http.createServer(this.handle.bind(this));
server.listen(...args);
}
}
// 解析 JSON 格式请求体
const bodyParserJSON = function (req, res, next) {
let data = "";
req.on("data", function (chunk) {
data += chunk;
});
req.on("end", function () {
try {
req.body = JSON.parse(data);
next();
} catch (err) {
res.statusCode = 400;
return res.end("bad request.");
}
});
};
// 解析表单请求体
const bodyParserUrlencoded = function (req, res, next) {
let data = "";
req.on("data", function (chunk) {
data += chunk;
});
req.on("end", function () {
req.body = qs.parse(data);
next();
});
};
// 设置响应头
const setContentType = function (req, res, next) {
res.setHeader("Content-Type", "text/html;charset=utf-8");
next();
};
// 全局错误处理中间件
const errorHandler = function (err, req, res, next) {
console.error(err);
res.statusCode = 500;
res.end("Internal Server Error");
};
let app = new Application();
// GET /
app.use("/", function (req, res) {
res.end("Hello World!");
});
// POST /api/user
app.use("/api/user", bodyParserJSON, function (req, res) {
res.end(`Hello ${req.body.name}!`);
});
app.use(setContentType);
app.use(bodyParserUrlencoded);
app.use(errorHandler);
app.listen(3000, function () {
console.log("Server started, listening on port 3000.");
});
运行上述代码后,在浏览器中访问 http://localhost:3000
将会返回 Hello World!
,在 Postman 等工具中发送 POST 请求,请求体包含一个 name
字段,则会返回 Hello xxx!
。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:JavaScript 实现类似Express的中间件系统(实例详解) - Python技术站