再学习express的时候,对其中间件的实现原理狠感兴趣。自己私下模拟了一个简易demo,分享给大家。下面直接上代码。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97const http = require('http');
const path = require('path');
class MiniExpress {
constructor() {
// 收集app 的中间件 路由执行 函数
this.routes = {
all: [], // 所有的匹配路径的请求 都执行的 中间件函数
get: [], // get 请求 匹配的 中间件执行函数
post: [] // post 请求
// put delete 可自行扩展
}
// 初始中间件
this.use(function (req, res, next) {
// 实现 json 方法
res.json = function (obj = {}) {
res.setHeader('Content-type', 'application/json');
res.end(JSON.stringify(obj))
}
next()
})
}
// 第一个参数可能是路径 也可能是函数,并且 支持 多个函数操作
use(...args) {
this.handleLayer('all', ...args)
}
handleLayer(method = 'all', ...args) {
let layer;
if (typeof args[0] == 'string') {
layer = {
path: args.shift(),
stack: args,
}
} else {
layer = {
path: '/',
stack: args
}
}
this.routes[method].push(layer)
}
get(...args) {
this.handleLayer('get', ...args)
}
post() {
this.handleLayer('post', ...args)
}
// 请求方式和路径 匹配 合适 的中间件
match(method, url) {
let midderwares = [];
// 处理浏览器 网页 图标
if (url == '/favicon.ico') {
return midderwares
}
// 处理 对所有请求方式 都生效的中间件
this.routes.all.filter(m => url.indexOf(m.path) == 0).forEach(m => {
midderwares = midderwares.concat(m.stack)
});
// 处理对应请求方式
const res = this.routes[method].filter(m => url.indexOf(m.path) == 0).forEach(m => {
midderwares = midderwares.concat(m.stack)
});
return midderwares;
}
// 处理请求的方法
hanldRequest(req, res) {
// console.log(req)
let method = req.method.toLocaleLowerCase(),
url = req.url;
const stack = this.match(method, url);
// 依次执行 中间件 数组
function next() {
const curMidware = stack.shift();
// 如果是 处理函数
if (curMidware) {
// 传入正常中间件的3个参数
curMidware(req, res, next);
}
}
next()
}
listen(...res) {
const server = http.createServer(this.hanldRequest.bind(this));
server.listen(...res);
}
}
module.exports = MiniExpress;
测试代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38const MiniExpress = require('./mini_express');
const app = new MiniExpress();
app.use(function (req, res, next) {
console.log('11111')
next()
})
app.use('/api/list', function (req, res, next) {
console.log('22222222222222')
next()
})
app.get('/api/list', function check(req, res, next) {
console.log('user is pass checked');
next()
}, function (req, res, next) {
res.json({
code: 200,
list: [
{ id: 1, text: 'ddd' },
{ id: 2, text: 'eee' }
]
})
})
app.listen(8000, () => {
console.log('server is listening at port 8000');
})
/*
测试 结果
server is listening at port 8000
11111
22222222222222
user is pass checked
*/
要实现express的中间件这样的效果,我们就需要记录所有的中间处理函数及其对应的路径和请求方式。然后,对每一次请求都匹配对应处理函数,最后就会得到每次请求的 中间件函数队列,然后利用指针来依次执行所有的中间件处理函数。这种思想也可用于异步和动画队列等问题的应用中。