中间件是处在HTTP的Request和Response中间,用来实现某种中间功能的函数。例如IP筛选,查询字符串传递,请求体解析,cookie信息处理,权限校验,日志记录,会话管理中间件(session),gzip压缩中间件(如compress),错误处理,这样与业务本身关联不强,却又需要公共抽象的模块。
使用nodemon监听文件的变化热更新提升开发体验。稍微设置一下延迟时间,减少无效保存操作影响。
"dev":"nodemon --delay 250ms ./app.js",
基于vscode 调试工具进行开发提效
const http = require('http')
const port = 9527
// http服务封装类
class Middleware {
constructor() {}
listen(port) {
this.httpModel = http.createServer((req, res) => {
res.end("hello world")
}))
this.httpModel.listen(port)
}
}
// 实例化对象
const app = new Middleware()
// 启动监听
app.listen(port)
常见的express和koa都是采用类似的方式实例化的可以查看下面的代码示例,不仅仅是应该开源团队成员的原因,实际上本身http模块就是使用这样的形式去创建服务的。
const express = require('express')
const app = express()
const port = 3000
app.get('/', (req, res) => {
res.send('Hello World!')
})
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})
const Koa = require('koa');
const app = new Koa();
app.use(async ctx => {
ctx.body = 'Hello World';
});
app.listen(3000);
路由的实际本身其实就是一个map映射表,对应的路径匹配可以执行的函数。实现起来还是比较简单。
const http = require('http')
const port = 9527
// http服务封装类
class Middleware {
constructor() {}
// 关系存储
getRouters = new Map()
// 监听端口方法
listen(port) {
this.httpModel = http.createServer((req, res) => {
// 如果是get请求
if (req.method === 'GET') {
// 获取map关系
if (this.getRouters.get(req.url)) {
// 执行回调
this.getRouters.get(req.url)(req, res)
} else {
res.end()
}
} else {
}
}
}))
this.httpModel.listen(port)
},
// get请求map关系建立
get(url, callback) {
this.getRouters.set(url, callback)
}
}
// 实例化对象
const app = new Middleware()
// 拦截请求
app.get('/api/getUser', (req, res) => {
res.end(JSON.stringify({ name: 'wuwenzhou' }))
})
// 启动监听
app.listen(port)
中间件可以说是面向切片的最佳实践了,这种在运行时动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程,在不改变原有业务行为的前提下,扩展了本身的场景,再不改变原来的代码的前提下完成我们的开发诉求。具体场景可以是一个日志中间件,支持跨域中间件。
从模型我们也可以看出两个特点
从模型本身来看我们需要一个有序数组,我们还需要控制流程。实现原理还是用了一个简单的递归去控制流程,加上next函数的执行,实现了请求由外入内,再又内到外。
const http = require('http')
const port = 9527
// http服务封装类
class Middleware {
constructor() {}
// 中间件集合
mids = []
use(midFn) {
this.mids.push(midFn)
}
// 关系存储
getRouters = new Map()
// 监听端口方法
listen(port) {
this.httpModel = http.createServer((req, res) => {
let index = 0
const midHandler = () => {
const next = () => {
index++
if (this.mids[index]) {
midHandler()
} else {
if (req.method === 'GET') {
if (this.getRouters.get(req.url)) {
this.getRouters.get(req.url)(req, res)
} else {
res.end()
}
} else {
}
}
}
if (this.mids[index]) {
this.mids[index](req, res, next)
} else {
if (req.method === 'GET') {
if (this.getRouters.get(req.url)) {
this.getRouters.get(req.url)(req, res)
} else {
res.end()
}
} else {
}
}
}
midHandler()
})
this.httpModel.listen(port)
},
// get请求map关系建立
get(url, callback) {
this.getRouters.set(url, callback)
}
}
// 实例化对象
const app = new Middleware()
// 日志中间件
const logger = (req, res, next) => {
console.log('开始记录日志')
const start = Date.now()
next()
const ms = Date.now() - start
console.log(req.method, req.url, res.statusCode, `处理耗时${ms}ms`)
}
// 支持跨域中间件
const cors = (req, res, next) => {
console.log('支持了跨域')
res.writeHead(200, {
'Access-Control-Allow-Origin': '*',
'Content-Type': 'application/json;charset=utf-8',
})
next()
}
//添加中间件
app.use(logger)
app.use(cors)
// 拦截请求
app.get('/api/getUser', (req, res) => {
res.end(JSON.stringify({ name: 'wuwenzhou' }))
})
// 启动监听
app.listen(port)
这仅仅是一个最简单的实现,帮助大家了解一下一个node服务的创建,路由的实现,中间件中控制,核心的很多场景都是没有实现的例如异步的支持,安全的校验,执行的去重,异常的处理等等。正在开发中大家还是要细化场景,完善整个逻辑。