实现Express中间件原理
文章目录
一、定义一个Express class
class Express {
constructor() {
// 存放路由中间件 此处只演示get、post方法
this.routes = {
all: [],
get: [],
post: [],
}
}
二、基本的use、get、post方法
1、register方法帮助注册中间件
/**
* 用于当调用实例的use、get、post等方法时注册中间件(处理路径)
*/
register(path) {
/**
* 可能传入多个参数,同时第一个参数可能是一个请求路径,对该路径进行处理
*/
const info = {}
if (typeof path === "string") {
/**
* 如果第一个参数传入的是路径则截取出第一个参数以外的参数作为中间件存入
*/
info.path = path
info.middleware = slice.call(arguments, 1)
} else {
/**
* 如果第一个参数没有传入路径则默认未'/'根路径,即所有请求都会经过该中间件的处理
*/
info.path = "/"
info.middleware = slice.call(arguments, 0)
}
return info
}
2、方法实现
/**
* 在所有调用注册use或者路由注册的方法中执行register帮助处理参数并注册
*/
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监听请求的具体实现
1、listen方法
listen(...args) {
const server = http.createServer(this.callback())
server.listen(...args)
}
2、listen方法依赖的callback
callback() {
return (req, res) => {
/**
* 模拟实现res.json,方便测试
*/
res.json = (data) => {
res.setHeader("Content-Type", "application/json")
res.end(JSON.stringify(data))
}
// 根据method和url匹配对应的路由以及中间件
const { url, method } = req
const middlewareList = this.matchMiddleware(url, method)
// 将匹配到中间件交给middlewareHandler执行
this.middlewareHandler(middlewareList)
}
}
3、匹配路由中间件以及中间件执行的实现
1、matchMiddleware匹配中间件
/**
* 匹配指定请求的路由以及中间件
* @param {请求的路径} url
* @param {请求的方法} method
*/
matchMiddleware(url, method) {
let middlewareList = []
if (url === "/favicon.ico") {
return middlewareList
}
/**
* 先匹配请求的method
*/
method = method.toLowerCase()
const currentRoutes = [...this.routes.all, ...this.routes[method]]
/**
* 再根据请求路径匹配
*/
middlewareList = currentRoutes.map((route) => {
if (route.path.indexOf(url) === 0) {
return route.middleware
}
})
return middlewareList
}
2、middleHandler中间件执行方法
middlewareHandler(req, res, middlewareList) {
const next = () => {
// 取出第一个中间件
const middleware = middlewareList.shift()
// 执行该中间件函数,
//并传入next,当该中间件执行该next函数时会接着取到下一个中间件,
//以此类推执行下去,便实现了中间件依次连续执行
middleware(req, res, next)
}
// 第一次传入中间件,马上执行
next()
}
四、完整代码
const http = require("http")
const slice = Array.prototype.slice
class Express {
constructor() {
// 存放路由中间件 此处只演示get、post方法
this.routes = {
all: [],
get: [],
post: [],
}
}
/**
* 用于当调用实例的use、get、post等方法时注册中间件(处理路径)
*/
register(path) {
/**
* 可能传入多个参数,同时第一个参数可能是一个请求路径,对该路径进行处理
*/
const info = {}
if (typeof path === "string") {
/**
* 如果第一个参数传入的是路径则截取出第一个参数以外的参数作为中间件存入
*/
info.path = path
info.middleware = slice.call(arguments, 1)
} else {
/**
* 如果第一个参数没有传入路径则默认未'/'根路径,即所有请求都会经过该中间件的处理
*/
info.path = "/"
info.middleware = slice.call(arguments, 0)
}
return info
}
/**
* 在所有调用注册use或者路由注册的方法中执行register帮助处理参数并注册
*/
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)
}
/**
* 匹配指定请求的路由以及中间件
* @param {请求的路径} url
* @param {请求的方法} method
*/
matchMiddleware(url, method) {
let middlewareList = []
if (url === "/favicon.ico") {
return middlewareList
}
/**
* 先匹配请求的method
*/
method = method.toLowerCase()
const currentRoutes = [...this.routes.all, ...this.routes[method]]
/**
* 再根据请求路径匹配
*/
middlewareList = currentRoutes.map((route) => {
if (route.path.indexOf(url) === 0) {
return route.middleware
}
})
return middlewareList
}
middlewareHandler(req, res, middlewareList) {
const next = () => {
// 取出第一个中间件
const middleware = middlewareList.shift()
// 执行该中间件函数,
//并传入next,当该中间件执行该next函数时会接着取到下一个中间件,
//以此类推执行下去,便实现了中间件依次连续执行
middleware(req, res, next)
}
// 第一次传入中间件,马上执行
next()
}
callback() {
return (req, res) => {
/**
* 模拟实现res.json,方便测试
*/
res.json = (data) => {
res.setHeader("Content-Type", "application/json")
res.end(JSON.stringify(data))
}
// 根据method和url匹配对应的路由以及中间件
const { url, method } = req
const middlewareList = this.matchMiddleware(url, method)
// 将匹配到中间件交给middlewareHandler执行
this.middlewareHandler(middlewareList)
}
}
listen(...args) {
const server = http.createServer(this.callback())
server.listen(...args)
}
}
module.exports = () => {
return new Express()
}
总结
中间件的重要机制首先是注册,然后是如何去执行中间件以及next()的实现,如何借助next()去控制各个中间件之间的链接,这里我主要依赖于middleHandler方法的实现。