示例代码托管在:http://www.github.com/dashnowords/blogs
在中间件系统的实现上,KOA
中间件通过async/await
来在不同中间件之间交换控制权,工作机制和栈结构非常相似,建议结合《express中间件系统的基本实现》对比学习,两个框架所基于的语法特性有区别(express
使用ES5
的回调风格语法,KOA
使用ES7
的扁平式异步async/await
风格语法),但在框架基本原理上是很类似的,只是中间件写法和遍历机制稍有不同。
一. API层
- 初始化方法 let middleware = new MiddleWare();
- 添加中间件函数的方法 //Fn为被添加的中间件,KOA中间件为async函数 middleware.use(Fn);
- 预处理中间件栈 //将存储于数组中的各个中间件组合为按照“先进后出”原则执行的中间件系统。 middleware.start = middleware.compose();
- 启动中间件队列 middleware.start(ctx);
二. 核心类的定义
代码语言:javascript复制/*
* KOA中间件框架的基本实现
*/
class MiddleWare {
constructor(){
this.queue = []
}
//添加中间件函数
use(fn){
this.queue.push(fn);
}
//合并中间件处理流,是一个高阶函数,调用一次后会生成真正需要的函数。
compose(){
return function (ctx, next) {
let _this= this;
let index = -1;
return dispatch(0);
/**
* KOA中间件的工作的步进函数
*/
function dispatch(i) {
index = i;
//依次取用数组中添加的中间件函数
let fn = i === _this.queue.length ? next : _this.queue[i];
if(!fn){
return Promise.resolve();
}
try{
/*
*中间件函数的形式为 async fn(ctx, next),可以看到此处透传了ctx的引用,
*同时next是一个延迟执行中间件队列中下一个中间件的函数,也就是说如果在前
*一个中间件的函数体中调用 await next(),就会启动下一个中间件,实际执行
*的函数是dispatch(i 1)。
*/
return Promise.resolve(fn(ctx,()=>{
return dispatch(i 1);
}));
}catch(err){
return Promise.reject(err);
}
}
}
}
}
三. 使用use方法添加中间件
代码语言:javascript复制//添加回调函数
middleware.use(async function(ctx, next){
console.log('step 001');
ctx.info = 'go through middleware1';
await next();
console.log('step 006');
});
middleware.use(async function(ctx, next){
console.log('step 002');
await next();
console.log('step 005');
});
middleware.use(async function(ctx, next){
console.log('step 003');
await next();
console.log('step 004');
});
四. 中间件实例
代码语言:javascript复制//初始化
let middleware = new MiddleWare();
/*
...此处为添加中间件的代码
*/
middleware.start = middleware.compose();
五. 查看运行结果
可以看到有错误发生和正常响应时的不同结果:
六. 在服务器端运行
用node
起一个web服务器那真是太随意了~
//启动http服务
http.createServer(function(req, res){
console.log(req.url);
let info = {};
middleware.start(info);
res.end(JSON.stringify(info));
}).listen(9527);
看一下效果(访问服务器时自定义消息就可以传至前台了):