express
express的实现
- 使用app.use注册中间件, 先收集起来
- 遇到http请求, 根据path, method判断触发哪些
- 通过next()执行下一步
const http = require('http');
const slice = Array.prototype.slice
class LikeExpress {
constructor() {
// 存放中间件的列表
this.routes = {
all: [], // 存放用use函数注册的中间件
get: [], // 用get函数注册的中间件
post: [], // 用post函数注册的中间件
put: [], // 等等http方法
}
}
// 通用注册中间件的方法
register (path) {
const 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
}
// 实现 app.use()方法
use () {
// 将当前函数形参全部传入, 返回info, path 和注册的所有中间件
const info = this.register.apply(this, arguments);
this.routes.all.push(info);
}
get () {
// 将当前函数形参全部传入, 返回info, path 和注册的所有中间件
const info = this.register.apply(this, arguments);
this.routes.get.push(info);
}
post () {
// 将当前函数形参全部传入, 返回info, path 和注册的所有中间件
const info = this.register.apply(this, arguments);
this.routes.all.post(info);
}
// 根据请求方式/请求地址筛选, 返回符合本次请求的所有中间件
match (method, url) {
let stack = [];
if (url === '/favicon.ico') {
return stack;
}
// 获取routes
let curRoutes = [];
// 通过use注册的中间件, 都需要调用
curRoutes = curRoutes.concat(this.routes.all)
// 通过get/post的, 直接用method区分, 筛选请求方式
curRoutes = curRoutes.concat(this.routes[method])
curRoutes.forEach(routeInfo => {
// 筛选url
// url === '/api/list'
if (url.indexOf(routeInfo.path) === 0) {
// routeInfo.path === '/' || '/api' || '/api/lis',都符合, 都需要加进去执行
stack = stack.concat(routeInfo.stack);
}
})
return stack
}
// 核心next机制
handle (req, res, stack) {
const next = () => {
// 拿到第一个匹配的中间件
const middleware = stack.shift();
if (middleware) {
// 执行中间件函数, 递归
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));
}
const url = req.url;
const method = req.method.toLowerCase(); // 把method变成小写
// 通过method和url, 筛选出符合本次请求的, 所有中间件, 然后都需要执行
// 已经匹配好的中间件列表
const resultList = this.match(method, url);
this.handle(req, res, resultList);
}
}
listen (...args) {
// 直接就创建server服务
const server = http.createServer(this.callBack());
// 把port什么的直接结构到了服务对象的listen中
server.listen(...args);
}
}
// 工厂函数, 确保每次都产生express
module.exports = () => {
return new LikeExpress()
}
express的中间件机制
- 根据上一步express的实现, express中间件的实现
- app.use()可同时注册三个中间件
const express = require('express');
// 本次http的实例
const app = express();
// 第一个参数没有路由, 那所有路由都会执行一次
// 执行完才能next(), 执行下一步
app.use((req, res, next) => {
console.log('请求开始', req.method, req.url);
next();
})
// 模拟处理cookie的中间件, next()
app.use((req, res, next) => {
// 假设处理cookie
req.cookies = {
sessionid: 123
}
next();
})
// 模拟异步处理post data的中间件, next()
app.use((req, res, next) => {
// 假设处理post data
// 异步
setTimeout(() => {
req.body = {
a: 12,
b: 123
}
next();
})
})
// 匹配到'/api'接口的都执行一遍, next()
app.use('/api', (req, res, next) => {
console.log(`处理'/api'路由`)
next();
})
// 匹配到 get '/api'都执行一遍, next()
app.get('/api', (req, res, next) => {
console.log(`get /api路由`)
next();
})
app.post('/api', (req, res, next) => {
console.log(`post /api 路由`)
next();
})
// 模拟登陆验证
const loginCheck = (req, res, next) => {
setTimeout(() => {
// console.log('登陆失败');
// res.json({
// errno: -1,
// msg: '登陆失败'
// })
next()
console.log('登陆成功');
})
}
// 匹配到get '/api/get-cookie'执行, 是否登录, 登录才能next()
// loginCheck 也算中间件
app.get('/api/get-cookie', loginCheck, (req, res, next) => {
res.json({
errno: 0,
data: req.cookies
})
// 没有next了
// next();
})
app.post('/api/get-body', (req, res, next) => {
res.json({
errno: 0,
data: req.body
})
// 没有next了
// next();
})
// 未匹配的的, 设置404
app.use((req, res, next) => {
console.log('处理404');
res.json({
errno: -1,
msg: '404 not found'
})
})
app.listen(3000, () => {
console.log('ok');
})
网友评论