Koa

写在前面

工作后很久没有更新博文了,一些事看不惯,一些事改变不了,从开始积极地想要做些什么,到现在连伪工作都算不上,让自己不得不好好想想了。回过头来看自己工作这半年多,能说道说道的也只有混淆了(ps.接触了混淆,真心觉得混淆这门艺术高大上),不过不能在这里分享,就只能在这里巩固拓展学习了~

Koa

koaexpress的原班人马打造,和express相比,koa是一个轻量级的node框架,它的所有功能都能通过中间件实现,这种插拔式的架构设计模式,很符合unix哲学。

有关unix哲学,核心是简单原则,可以看这篇以及文末的评论。

想到最近实现的一个混淆策略,已经在实现第二版,但代码还是越写越绕,mentor在发现我第二版的混淆策略仍然有处理不了的样例时说让我改的下一版更优雅些、代码易懂一点。代码越写越绕的原因应该是写的过程中发现需要兼容到某几种特殊样例的情况,每次为了兼容这种情况都会在原有基础上改,改到后面看见那一坨坨的代码就晕。。。感觉还是自己思考时站的高度不够。

目前所用的框架是koa2,与koa1不同,koa1使用的是generator+co的执行方式,而koa2使用的是async/await,所以需要node v7.6.0更高版本对async的支持。

async/await

async函数允许你把异步的代码(当然这个异步方法支持promise时)当成同步的去对待,可读性高一些。在async function内部使用await来暂停async function的执行,等待一个promise对象处理完成。await返回需要是Promise对象。

先来一个最简单的koa样例:

1
2
3
4
5
6
7
8
9
const Koa = require('koa');
const app = new Koa();
app.use(async (ctx, next) => {
ctx.body = 'Hello World';
await next();
});
app.listen(3000);

启了一个node http server,监听端口3000app.use中维护了一个中间件队列,代码中的这个中间件的功能是对请求curl http://localhost:3000响应内容hello world

next()不是必须的,用来在当前中间件中想要downstream到下一个中间件时调用。

Middleware onion diagram

下面图中很清晰的表明了一个请求是如何经过中间件最后生成响应的:

onion

理解洋葱模型怎么运作,来看下面的样例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const Koa = require('koa');
const app = new Koa();
// middleware0
app.use(async (ctx, next) => {
console.log(0);
const a = await next(); // 这里是执行了middleware1之后return的结果
console.log(a);
return 'middleware0';
})
// middleware1
app.use(async (ctx, next) => {
console.log(1);
const b = await next(); // 这里是执行了middleware2之后return的结果
console.log(b);
return 'middleware1';
})
// middleware2
app.use(async (ctx, next) => {
console.log(2);
const c = await next(); // 这里因为没有下一个中间件的返回所以为undefined
console.log(c);
return 'middleware2';
})
app.listen(3000);

输出结果为:

1
2
3
4
5
6
0
1
2
undefined
middleware2
middleware1

每个中间件都接受一个koa context object和一个next function作为参数,调用next函数会执行到下一个中间件中(如果在middleware1中注释掉next调用,就会发现并不会执行到middleware2中)。

其中await next()的作用是当程序运行到它时会暂停当前中间件的执行,进入下一个中间件,等后面的中间件都处理完之后再回过头来处理。也就是说,当一个请求发来时,middleware0是第一个进入最后一个离开。

特殊地,koa实现了对context的保存和传递(将context一路传下去给中间件),每个中间件的next返回值是下一个中间件的返回值。

koa这种洋葱结构,可以很方便的进行错误处理。

这里提一下,在回调函数中抛出错误,如果没有任何错误捕获处理,node进程会崩溃。同时对回调函数try/catch也是没有用的,是捕获不到的,解决方法一个是监听error事件,在koa中的解决方法就是在外层中间件对错误进行捕获。可以看koa demo #Demo16: error handling & #Demo17: error listener

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const Koa = require('koa');
const app = new Koa();
// middleware error catch
app.use(async (ctx, next) => {
try{
await next();
}catch(e){
console.log(e);
}
})
// middleware error test1
app.use(async (ctx, next) => {
throw new Error('error test1');
});
// middleware error test2
app.use(async (ctx, next) => {
await new Promise((resolve, reject) => {
reject(new Error('error test2'));
})
})
app.listen(3000);
console.log('server listening...');

在所有中间件中抛出的错误都可以在第一个中间件中捕获到,这样就可以包装一个统一的错误处理中间件。

Tips

  • koa的一些demo
  • koa context api
  • koa-bodyparser:解析post请求的body数据
  • koa-static:把静态文件serve出去。针对koa demo #Demo12: static assets的样例,访问http://localhost:3000/12.js页面上显示文件内容,而正常未使用koa-static的访问时会显示Not Found
  • koa-router:路由,样例:
1
2
3
4
5
6
7
8
9
// router.js
const Router = require('koa-router');
const router = new Router();
const fs = require('fs');
router.get('/test', (ctx, next) => {
console.log('received request....');
ctx.body = 'test';
})
module.exports = router;
1
2
3
4
5
6
7
// index.js
const Koa = require('koa');
const app = new Koa();
const router = require('./router');
app.use(router.routes()).use(router.allowedMethods());
app.listen(3000);
console.log('server listen on port 3000');
文章目录
  1. 1. 写在前面
  2. 2. Koa
    1. 2.1. async/await
    2. 2.2. Middleware onion diagram
    3. 2.3. Tips
|