最简Koa.js实现
前言
从上一章可以看到最简单的中间件式HTTP服务的实现,底层是基于回调嵌套去处理中间件队列。
/**
* 中间件总回调方法
*/
callback() {
let that = this;
if (this.listeners('error').length === 0) {
this.on('error', this.onerror);
}
const handleRequest = (req, res) => {
let context = that.createContext(req, res);
this.middleware.forEach((cb, idx) => {
try {
cb(context);
} catch (err) {
that.onerror(err);
}
if (idx + 1 >= this.middleware.length) {
if (res && typeof res.end === 'function') {
res.end();
}
}
});
};
return handleRequest;
}
但是中间件越多,回调嵌套越深,代码的可读性和可扩展性就很差,所以这时候把回调嵌套转化成 Promise
+ async/await
,这个时候就转变成最简单的Koa.js
实现。
必要条件
- 通过上下文赋值可代替
res.end()
- 洋葱模型的中间件机制
源码实现
- demo源码
https://github.com/chenshenhai/koajs-design-note/tree/master/demo/chapter-01-07
- 最简Koa.js 实现
const http = require('http');
const Emitter = require('events');
// 注意:这里的compose是前几章的中间件引擎源码
const compose = require('./../compose');
/**
* 通用上下文
*/
const context = {
_body: null,
get body() {
return this._body;
},
set body(val) {
this._body = val;
this.res.end(this._body);
}
};
class SimpleKoa extends Emitter {
constructor() {
super();
this.middleware = [];
this.context = Object.create(context);
}
/**
* 服务事件监听
* @param {*} args
*/
listen(...args) {
const server = http.createServer(this.callback());
return server.listen(...args);
}
/**
* 注册使用中间件
* @param {Function} fn
*/
use(fn) {
if (typeof fn === 'function') {
this.middleware.push(fn);
}
}
/**
* 中间件总回调方法
*/
callback() {
if (this.listeners('error').length === 0) {
this.on('error', this.onerror);
}
const handleRequest = (req, res) => {
let context = this.createContext(req, res);
let middleware = this.middleware;
// 执行中间件
compose(middleware)(context).catch(err => this.onerror(err))
};
return handleRequest;
}
/**
* 异常处理监听
* @param {EndOfStreamError} err
*/
onerror(err) {
console.log(err);
}
/**
* 创建通用上下文
* @param {Object} req
* @param {Object} res
*/
createContext(req, res) {
let context = Object.create(this.context);
context.req = req;
context.res = res;
return context;
}
}
module.exports = SimpleKoa;
- 执行例子
const SimpleKoa = require('./index');
const app = new SimpleKoa();
const PORT = 3001;
app.use(async ctx => {
ctx.body = '<p>this is a body</p>';
});
app.listen(PORT, () => {
console.log(`the web server is starting at port ${PORT}`);
});