中间件是一种拦截器的思想,用于在某个特定的输入输出之间添加一些额外的处理,它最大的特点就是,一个中间件处理完,再传递给下一个中间件,App实例在运行过程中,会调用一系列的中间件。
每个中间件可以从App实例中接收三个参数,依次是request对象(请求)、response对象(响应)、next函数(执行下一个中间件),每一个匹配的中间件都可以对request对象进行加工,并且决定是否调用next方法来执行下一个中间件。
中间件是在管道中执行的,你可以想象一个真实的水管道,水从一端注入,到达另一端之前会经过各种仪器、阀门,这里的顺序也是很重要的一点。在express中,调用app.use可以插入一个新的管道。
分析一下中间件的原理:
1.app.use用来注册中间件,先收集起来;
2.遇到HTTP请求,根据path和method进行匹配,判断触发哪些中间件;
3.next机制,即上一个通过next触发下一个;
那么,我们就来简单用代码实现一下:
const http = require('http')
const slice = Array.prototype.slice
class EasyExpress {
constructor() {
this.routes = {
all: [],
get: [],
post: []
}
}
// 注册中间件
register(path) {
let info = {}
// 判断第一个参数是否为路由
if (typeof path === 'string') {
info.path = path
info.stack = slice.call(arguments, 1)
} else {
info.path = '/'
info.stack = slice.call(arguments, 0)
}
return info
}
use() {
const info = this.register.apply(this, arguments)
this.routes.all.push(info)
}
get() {
const info = this.register.apply(this, arguments)
this.routes.get.push(info)
}
post() {
const info = this.register.apply(this, arguments)
this.routes.post.push(info)
}
listen(...args) {
const server = http.createServer(this.callback())
server.listen(...args)
}
callback() {
return (req, res) => {
let { url, method } = req
method = method.toLowerCase()
let stack = []
const routeList = []
routeList = routeList
.concat(this.routes.all)
.concat(this.routes[method])
// 寻找匹配的中间件
routeList.forEach(routeInfo => {
if (url.indexOf(routeInfo.path) === 0) {
stack = stack.concat(routeInfo.stack)
}
})
this.handler(req, res, stack)
}
}
// 处理stack
handler(req, res, stack) {
const next = () => {
const fn = stack.shift()
if (fn) {
fn(req, res, next)
}
}
next()
}
}
next 方法不断的取出stack中的“中间件”函数进行调用,同时把next 本身传递给“中间件”作为第三个参数,每个中间件约定的固定形式为 (req, res, next) => {}, 这样每个“中间件“函数中只要调用 next 方法即可传递调用下一个中间件。