本文主要从以下几个方面来阐述koa源码
- koa的context应用
- koa 中间件机制
- 错误处理
context应用
koa为了简化API,引入了context概念,将原始请求对象req和响应对象res 封装并且挂载到context上 在context上设置getter 和 setter 从而简化操作。
createContext(req, res) {
const ctx = Object.create(context)
ctx.request = Object.create(request)
ctx.response = Object.create(response)
ctx.req = ctx.request.req = req
ctx.res = ctx.response.res = res
return ctx
}
Object.create 是因为context,request,response对象了里面有getter setter操作
request.js
module.exports = {
get url() {
return this.req.url;
},
get method() {
return this.req.method.toLowerCase()
}
};
response.js
module.exports = {
get body() {
return this._body
},
set body(val) {
this._body = val
}
}
context.js
module.exports = {
get url() {
return this.request.url
},
get body() {
return this.response.body
},
set body(val) {
this.response.body = val
},
get method() {
return this.request.method
}
}
当我调用ctx.method的时候
ctx.method ->
context.js / return this.request.method
ctx.request.method ->
request.js / this.req.method.toLowerCase()
ctx.request.req.method.toLowerCase() ->
ctx.req = ctx.request.req = req ->
req.method.toLowerCase()
koa 中间件机制
中间件就是函数式编程的compose应用 将一组异步函数 复合成一个函数 内层函数的返回值就是外层函数的参数
下图是经典的洋葱圈模型
koa-compose代码如下
compose(middlewares) {
return function (ctx) {
return dispatch(0)
function dispatch(i) {
let fn = middlewares[i]
if (!fn) {
return Promise.resolve()
}
return Promise.resolve(
fn(ctx, function next() {
return dispatch(i + 1)
})
)
}
}
}
采用递归来处理 最后大概变成这个样子
const [fn1, fn2, fn3] = this.middleware;
const fnMiddleware = function(context){
return Promise.resolve(
fn1(context, function next(){
return Promise.resolve(
fn2(context, function next(){
return Promise.resolve(
fn3(context, function next(){
return Promise.resolve();
})
)
})
)
})
);
};
fnMiddleware(ctx).then(handleResponse).catch(onerror);
调用的时候
app.use(async (ctx, next) => {
ctx.body = "1";
await next();
ctx.body += "5";
});
app.use(async (ctx, next) => {
ctx.body += "2";
await next();
ctx.body += "4";
});
上面代码await next() 的时候 await Promsie.resolve() 就是执行下一个中间件 等所有下一个中间件执行完了 再执行 next后面的代码。
koa错误处理
Promise报错通过catch来捕获,最后调用context里面的error emit一个error事件,然后application 里面接受一下 最后打印出来
callback() {
const fn = compose(this.middleware);
if (!this.listenerCount('error')) this.on('error', this.onerror);
....
}
application.js
const onerror = err => ctx.onerror(err);
fnMiddleware(ctx).then(handleResponse).catch(onerror);
--->
context.js
ctx.onerror
onerror(err) {
this.app.emit('error', err, this);
}
application.js
this.on('error', this.onerror);
onerror(err) {
const msg = err.stack || err.toString();
console.error();
console.error(msg.replace(/^/gm, ' '));
console.error();
}