express实战

express+swig+mysql+multer实战

Posted by grt1stnull on 2016-12-03

打hitcon2016时遇到了nodejs的题,挺有意思的;加上刚好在图书馆看到关于express和nodejs的书,于是想写一个试试。用了许多express组件,再整合了七牛和极验的sdk,写了一个文件上传再上传到七牛云的网站。

0x01.安装nodejs与npm

nodejs官网打不开,下载安装真是一件痛苦的事,不过wget多试几次总会成功的,比如崩了就wget -c 继续(老司机告诉我wget -t 0 -c就好了

1.npm使用国内淘宝源

国内源速度快,比默认不知高到了哪里去。谷歌了一下,很简单

1
2
3
4
5
6
$ npm config set registry https://registry.npm.taobao.org
// 配置后可通过下面方式来验证是否成功
$ npm config get registry
// 或
$ npm info express

2.nodejs版本管理n

当你想升级nodejs版本或使用多版本时可以使用n

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 检查 Node的当前版本,使用命令
$ node -v
// 安装n模块
$ sudo npm install -g n
// 选择一个流行版本安装
$ sudo n 0.8.11
// 安装最新的稳定版本
$ sudo n stable
// 查看所有node版本
$ sudo n ls
// 查看帮助
$ n help
//使用某一版本
$ n use 4.4.4

0x02.使用express

1
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
var express=require('express');
var app=express();
app.set('port',process.env.PORT||3000);
app.get('/',function(req,res){
res.type('text/plain');
res.send('index?');
});
app.get('/about',function(req,res){
res.type('tetx/plain');
res.send('anything else?');
});
app.use(function(req,res,next){
res.type('text/plain');
res.status(404);
res.send('404');
});
app.use(function(err,req,res,next){
console.error(err.stack);
res.type('text/plain');
res.status(500);
res.send('500-server error')
});
app.listen(app.get('port'),function(){
console.log('express statred at http://localhost:'+app.get('port'));
});

基本就是上面的套路哈哈,引用模块就var xxx=require(‘xxx’);app.listen监听端口,推荐使用这里的app.set,正式一点;接下来是路由,路由支持正则等等;下面res.status选择状态码,本例子给了404找不到和500服务器错误。

使用 node xxx.js运行

0x03.使用multer

1
$ npm install multer -g

1.基本使用

使用multer来接收文件上传,有表单和ajax两种方式。因为比较熟悉表单,所以没有使用ajax,虽然事实上ajax体验会好一点。multer.js在github上可以找到,帮助文档很简单,可以看懂,下面是简单使用。

1
2
3
4
5
6
var multer = require('multer');
var upload = multer({ dest: 'uploads/' });
app.post('/profile', upload.single('avatar'), function (req, res, next) {
//操作
});

其中,第二行是通常的设置,dest是文件保存位置;下面的/profile是后台处理地址,avatar是表单中input type=file的name属性值

2.定制

如果想要对上传文件进行一些限制和定制要使用storage,即:

1
2
3
4
5
6
7
8
9
10
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, '/tmp/my-uploads')
},
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now())
}
})
var upload = multer({ storage: storage })

对目录(destination)和文件名(filename)进行了定制

3.错误处理

上传文件时可能会发生错误,或是在上一步定制限制时会造成错误,这时就需要错误处理

1
2
3
4
5
6
7
8
9
10
11
12
13
var upload = multer().single('avatar')
app.post('/profile', function (req, res) {
upload(req, res, function (err) {
if (err) {
// An error occurred when uploading
next(500);
}
else{
// Everything went fine
}
});
});

0x04.七牛sdk使用

没有使用官方github的,直接npm install qiniu -g,然后使用帮助中心的上传代码,定制一下就好了

1
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
var qiniu = require("qiniu");
//需要填写你的 Access Key 和 Secret Key
qiniu.conf.ACCESS_KEY = 'Access_Key';
qiniu.conf.SECRET_KEY = 'Secret_Key';
//要上传的空间
bucket = 'Bucket_Name';
//上传到七牛后保存的文件名
key = 'my-nodejs-logo.png';
//构建上传策略函数
function uptoken(bucket, key) {
var putPolicy = new qiniu.rs.PutPolicy(bucket+":"+key);
return putPolicy.token();
}
//生成上传 Token
token = uptoken(bucket, key);
//要上传文件的本地路径
filePath = './ruby-logo.png'
//构造上传函数
function uploadFile(uptoken, key, localFile) {
var extra = new qiniu.io.PutExtra();
qiniu.io.putFile(uptoken, key, localFile, extra, function(err, ret) {
if(!err) {
// 上传成功, 处理返回值
console.log(ret.hash, ret.key, ret.persistentId);
} else {
// 上传失败, 处理返回代码
console.log(err);
}
});
}
//调用uploadFile上传
uploadFile(token, key, filePath);

0x05.使用mysql

以为express里会有什么特殊的mysql模块,并没有,直接npm install mysql -g

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var mysql = require('mysql');
var connection = mysql.createConnection({
host: 'localhost',
user: 'user',
password: 'passwd',
database: 'database'
});
connection.connect();
var query = connection.query('query语句');
query.on('error',function(err){
throw err;
});
connection.end();

0x06.使用极验

nodejs要使用验证码太麻烦了,再加加上滑动验证码比较有逼格,不自己造轮子,使用极验。

官方提供了sdk,但是使用起来真是太难了。看了两个小时总算艰难弄懂了,首先前端访问你的一个路由获得验证,接下来再在后来接收表单的地方再进行一次验证。代码如下:

1
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
var Geetest = require('./gt-sdk');
var bodyParser = require("body-parser");
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
var pcGeetest = new Geetest({
geetest_id: 'xxxxxxxxxxxxxxxxxxxxx',
geetest_key: 'xxxxxxxxxxxxxxxxxxxxx'
});
app.get("/register", function (req, res) {
pcGeetest.register(function (data) {
res.send(JSON.stringify({
gt: pcGeetest.geetest_id,
challenge: data.challenge,
success: data.success
}));
});
});
app.post('/upload',function(req,res,next){
pcGeetest.validate({
challenge: req.body.geetest_challenge,
validate: req.body.geetest_validate,
seccode: req.body.geetest_seccode
}, function(req,res,next){
//do things
}))
});

npm没安装到sdk,所以把官方github上的gt-sdk.js放在同一目录(还有package.json也要放在同一目录)还要npm insatll bode-parser request -g来解析json

1
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
<style type="text/css">
.show{display: block;}
.hide{display: none;}
</style>
<script src="http://code.jquery.com/jquery-1.12.3.min.js"></script>
<script src="http://static.geetest.com/static/tools/gt.js"></script>
<div id="embed-captcha"></div>
<p id="wait" class="show">正在加载验证码......</p>
<p id="notice" class="hide">请先拖动验证码到相应位置</p>
<script>
var handlerEmbed = function (captchaObj) {
$("#embed-submit").click(function (e) {
var validate = captchaObj.getValidate();
if (!validate) {
$("#notice")[0].className = "show";
setTimeout(function () {
$("#notice")[0].className = "hide";
}, 2000);
e.preventDefault();
}
});
captchaObj.appendTo("#embed-captcha");
captchaObj.onReady(function () {
$("#wait")[0].className = "hide";
});
};
$.ajax({
url: "/register?t=" + (new Date()).getTime(),
type: "get",
dataType: "json",
success: function (data) {
initGeetest({
gt: data.gt,
challenge: data.challenge,
product: "embed",
offline: !data.success
}, handlerEmbed);
}
});
</script>

0x07.使用swig

swig是模板引擎,国内资料太少啦,还是去github看官方文档,蛮简单的。

npm install swig -g下载安装,先在swig.js的express demo下npm start server试一下。

1
2
3
4
5
6
7
var swig=require('swig');
app.engine('html', swig.renderFile);
app.set('view engine', 'html');
app.set('views', __dirname);
res.render('static', {title: '404 Error!',});

__dirname为模板的位置,我直接放在当前目录了。使用res.render代替res.send发送数据,static指定模板为’static.html’,title为替代的模板内容,更多使用参考官方手册

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html>
<head>
<title>{{ title }}</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
</head>
<body>
</body>
</html>

0x08.使用fs

fs为对文件的操作,不想用模板引擎可以直接发送网页啊。这样用:

1
2
var index=fs.readFileSync('./index.html',{encoding:'utf-8'});
res.send(index);

不爽了也可以删

1
fs.unlink('./static.html');

0x09.配置nginx

因为想在80端口使用,但是80端口已布置了nginx,所以使用nginx进行端口转发。修改nginx的配置文件,在server外加入另一个server:

1
2
3
4
5
6
7
8
9
10
11
12
13
server{
listen 80;
server_name xxx.grt1st.cn;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_pass http://127.0.0.1:3000/;
proxy_redirect off;
}
}

server_name为域名,proxy_pass选择本地端口

可以使用nginx -t来查看当前配置文件情况

但是,当我配置好访问时却出现了502错误,查看日志发现访问denied,谷歌发现解决方法如下:

1
2
3
4
//错误信息
......[crit] 4584#0: *11 connect() to 127.0.0.1:3000 failed (13: Permission denied) while connecting to upstream, client: 127.0.0.1, server: blog.caesar.com, request: "GET / HTTP/1.1", upstream: "http://127.0.0.1:3000/" ......
//解决办法
$ setsebool -P httpd_can_network_connect 1

0x10.使用forever

就当我配置好后退出了ssh,结果nodejs的进程崩了。谷歌了一下,使用forever守护进程

1
2
3
$ npm install forever -g
forever start app.js

0x11.参考文献

Node.js 升级
通过命令升级nodejs版本
国内优秀npm镜像推荐及使用
multer/en-github
multer/zn-github
Node.js SDK 使用指南
node.js教学-操作MySQL 资料库
nginx查看配置文件nginx.conf路径
Nginx反向代理部署Node.js应用配置方法
解决nginx下connect() to 127.0.0.1:3000 failed (13: Permission denied) while connecting to upstream, client: 127.0.0.1, server: 错误信息
解决Nginx的connect() to 127.0.0.1:8080 failed (13: Permission denied) while connect
nodejs后台运行,退出 ssh 后仍然有效的方法
linux下让nodejs应用后台执行