对于使用过nodejs中express框架的同学应该很熟悉中间件是express的核心部分,接下来我们将中间件的实现尝试写一下.
在开发之前我先整理一下express的API,再通过这些API去反推它的实现:
//express的实例有哪些用法再反推它怎么实现.
//下面是伪代码部分
const app = new express();
app.use((req,res,next)=>{
next();
})
app.use("/api",(req,res,next)=>{
next();
})
app.post("/login",(req,res)=>{
res.json({result:1,msg:"我是post请求"});
})
function middleWare(req,res,next){
console.log("我是中间件");
next();
}
app.get("/getContent",middleWare,()=>{
res.json({result:1,msg:"我是get请求"});
})
app.listen(8080)
从上面的API我们可以看出需要实现几个方法,分别是:json,get,post,listen和use.其中我们格外要注意的是中间件前面还可以写多个中间件.
接下来就是核心的实现文件index.js
const http = require("http");
class express {
app = null;
funList = {
middleWare: [], //存储中间件的方法
get: {}, //存储get请求的方法
post: {}, //存储post请求的方法
all: {}
}
constructor() {
this.app = http.createServer((req, res) => {
res.json = (data) => {
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify(data));
}
if (req.url !== "/favicon.ico") {
this.requestHandler(req, res);
}
})
}
/**
* 核心方法
*
* 因为这个use方法可以定义多个,我觉得这个use方法是再收集回调函数
*
*/
use(...args) {
this.parameterVerify(args);
this.collectRequest(args);
}
/**
* 参数验证
*/
parameterVerify(args) {
if (args.length == 0) {
throw new Error("中间件参数不能为空!");
} else if (args.length == 1 && typeof args[0] !== "function") {
throw new Error("中间件函数不能为空!");
}
const middleWare = args[0];
if (typeof middleWare === "function") {
args.unshift("*");
}
}
collectRequest(args, method = null) {
const path = args[0];
const fun_arr = args.slice(1);
let arr = [];
fun_arr.forEach((fun) => {
arr.push({
path,
callback: fun
});
})
if (path === "*") {//这是个中间件
this.funList.middleWare = this.funList.middleWare.concat(arr);
} else if (method === null) { //任何请求都满足
this.funList.all[path] = arr;
} else if (method) { //为某种特定的方法
const method_lower = method.toLowerCase();
this.funList[method_lower][path] = arr;
}
}
/**
*
* @param {*} req
* @param {*} res
* 获取请求要运行的函数栈
*/
getStack(req) {
const { url, method } = req;
let arr = [...this.funList.middleWare];
try {
const data = this.funList[method.toLowerCase()][url];
arr = arr.concat((Array.isArray(data) ? data : []));
} catch (error) {
}
arr = arr.concat(this.funList.all[url] ? this.funList.all[url] : []);
return arr;
}
/**
* 处理请求
*/
requestHandler(req, res) {
let stacks = this.getStack(req);
const next = () => {
const fun = stacks.shift();
if (fun) {
fun.callback(req, res, next);
}
}
next();
}
/**
*
* @param {*} port
*/
get(...args) {
this.parameterVerify(args);
this.collectRequest(args, "GET");
}
/**
*
* @param {*} port
*/
post(...args) {
this.parameterVerify(args);
this.collectRequest(args, "POST");
}
/**
* 创建服务器的方法
*/
listen(port) {
this.app.listen(port);
}
}
module.exports = express;
分析:我们主要利用原生模块http的createServer方法来实现.在这里将定义的中间件分为四类:普通中间件函数,get请求方法,post请求方法和任意请求方法.将定义的中间件全部分类好装入这四类数据中.当用户的请求进来,通过req的url和method找到该请求需要运行的中间件函数链(在上面的四类数据中寻找),随后用嵌套的方式运行函数链,从而就实现中间件函数的依此执行.上面代码最核心的部分就是next方法的实现,通过shift移除数组的第一个元素不断嵌套运行next方法达到依此执行函数链的效果.
最后我们也可以写一个测试文件来测试上面代码test.js
const express = require("./index");
const app = new express();
app.use((req, res, next) => {
console.log(123);
next();
})
app.use((req, res, next) => {
console.log(456);
next();
})
function middleWare(req, res, next) {
setTimeout(() => {
console.log('789')
next()
}, 2000)
}
app.use(middleWare, middleWare, (req, res, next) => {
console.log('101112')
next();
})
app.use("/api", (req, res) => {
res.end("your request url is api");
})
app.get("/request", (req, res) => {
res.end("i'm is get");
})
app.post("/request", (req, res) => {
res.end("i'm is post");
})
app.use("/", (req, res) => {
res.json({ msg: "not found!" });
})
app.listen(8080);
最后:将test.js和index.js放在同一目录,在当前目录下打开命令行工具,输入node ./test.js启动node服务(当然前提是要安装好node环境),在浏览器输入http://localhost:8080/查看运行结果.