最近有需求需要用到node来写一些简易的cgi,帮助后台同学减轻压力。
用node写cgi有两个问题,一个是端口,我们不能每加一个cgi就新增一个端口;第二个是异步回调问题,实现中会遇到很多例如读DB、读文件的异步行为,多层的callback会让代码很繁琐且不易输出内容。
于是想着用最近很火的tj大神的Koa来搭建一套node-cgi框架。
先说说koa,koa是基于co的类似于express的web开发框架,了解koa首先要了解co,了解co首先要了解ES6的generator。
#1.generator
generator是ES6的新函数,写法如下:
1 | function* generator(){ |
generator使用yield的方法,让函数内部执行暂定,只有当调用next()方法的时候,才会接着往下走,同时返回一个对象,返回对象有两个值,一个是yield后面执行的结果,一个是done字段,如果yield都执行完了 done 字段就变为true。
使用yield来阻断函数内部执行,就可以将我们的异步函数转成同步执行。
#2. co
下来再看co,co帮助我们实现了自动调用next()方法,在co内部写yield,等待yield执行完毕就可以继续向下进行。
例如使用tof获取用户信息:
1 | co(function *() { |
#3. koa
koa和express一样,使用中间件:
1 | var koa = require('koa'); |
koa的Context 把 node 的 request, response 对象封装进一个单独对象, 并提供许多开发 web 应用和 APIs 有用的方法. 那些在 HTTP server 开发中使用非常频繁操作, 直接在 Koa 里实现, 而不是放在更高层次的框架, 这样中间件就不需要重复实现这些通用的功能。
1 | app.use(function *(){ |
需要输出的内容,直接赋值给this.body即可。
- node-cgi
了解完co和koa,我们就可以搭建一个简易的node-cgi框架,目录如下:
app.js作为入口,使用
node –harmony-generators app.js
来进行启动,在modules这块使用按需加载,拿到url里的参数后再加载对应的module:
1 | var cgi = require('./modules/' + cgiName); |
这样每次我们发布 只需替换modules文件夹里的js文件即可,不需要启停node服务(和php很像)。
而modules里的cgi文件,可以使用co来处理内部的异步回调,方法和koa相同,愉快同步向下写js从此开始了~
下面附上代码
app.js
1 | /** |
sns_super_white_list.js1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93/**
* @fileOverview 超级白名单cgi
* @version 1.0.0
* @author <a href="mailto:lucienxu@tencent.com">lucienxu</a>
* @date 2015/9/8
* @copyright Copyright (c) 2014, Tencent Inc. All rights reserved.
* @see [link]
*/
var Sequelize = require('sequelize');
var co = require('co');
var db = new Sequelize('mysql://root@127.0.0.1:3306/xxx');
module.exports = function (method, param, resp) {
/*
* 三个参数,method是get/post,param是传入参数,resp是输出数据
* resp(err, result);
* resp传入两个参数,第一个是错误信息,将通过console打印出来,第二项是输出的结果result,result为json格式
*/
co(function *() {
//db.sync();
db["t_misc_conf"] = db.define('t_misc_conf', {
key: {
type: Sequelize.STRING,
primaryKey: true,
},
value: Sequelize.TEXT,
}, {
timestamps: false,
freezeTableName: true,
});
console.log(method)
console.log(param)
var result;
if(method == 'get'){
console.log('method - get')
switch (param.action){
case 'get_white_list':
console.log('get_white_list')
result = yield *getWhiteList();
break;
}
}else if(method == 'post'){
console.log('method - post')
switch (param.action){
case 'set_white_list':
console.log('set_white_list')
result = yield *setWhiteList(param);
break;
}
}
resp(null, result);
})
function *getWhiteList(){
console.log('get_white_list---begin')
var result = yield function (done) {
db.t_misc_conf.findOne({where: {key: 'super_whitelist'}}).then(function (res) {
console.log('get_white_list--yield--done')
console.log(res.get('value'))
done(null, {"ret": 0, msg: 'success', "result": res.get('value')});
});
}
console.log('get_white_list---done')
return result;
}
function *setWhiteList(param){
console.log('set_white_list---begin')
var data = {
value: param.value
}
var result = yield function (done) {
db.t_misc_conf.update(data, {where: {key: 'super_whitelist'}}).then(function (res) {
console.log(res)
done(null, {"ret": 0, msg: 'success'});
});
}
console.log('set_white_list---done')
return result;
}
}