web应用后台终究还是要和数据库打交道,不然只是一堆写死在html里的东西。
我在工程中使用了bookshelf和knex两个框架分别作为ORM和Query Builder,之前也已经写过两篇博客bookshelf.js笔记、knex.js笔记来介绍具体的语法,就不再赘述,这里就讲讲具体到实践中我是怎么用的。
和JDBC类似,使用knex之前,需要将数据库的ip、端口、数据库名、登录账号和口令等基本信息配置到工程中,可以通过单独写配置文件将具体的保密信息隐藏,上传的时候设置好gitignore文件,不上传config即可:
const config = require('./config');
const knex = require('knex')({
client: 'pg',
connection: {
host: config.host,
user: config.username,
password : config.password,
database : config.database,
port: config.port
}
});
更科学的方法是,设置多个数据库配置文件,将生产、测试、开发的场景都区分开,根据运行模式动态选择要连接哪一个数据库。
const bookshelf = require('bookshelf')(knex);
const Users = bookshelf.Model.extend({
tableName: 'users'
});
const WafLogs = bookshelf.Model.extend({
tableName: 'waf_logs'
});
将knex作为参数用于创建bookshelf实例,tablename
用于指定该数据对象对应的数据表。
配置好ORM和Query Builder之后,就可以开始进行数据库操作了。
以用户注册的业务为例,来讲一下:
先上代码:
router.post('/reg', async function (ctx, next) {
if(ctx.request.body['username'].length > 25) {
//判断用户名是否过长,数据库设置username字段为varchar(25)
await ctx.render('reg', {
title: 'OA-注册',
error: '用户名不得超过25个字符'
});
} else if(ctx.request.body['password2'] !== ctx.request.body['password']) {
//判断两次密码是否一致
console.log('两次密码不一致');
await ctx.render('reg', {
title: 'OA-注册',
error: '两次密码不一致'
});
} else {
//判断用户名是否存在
var count = await Users.where('username', ctx.request.body['username']).count('username');
if(count != 0) {
console.log('用户名已存在!');
await ctx.render('reg', {
title: 'OA-注册',
error: '用户名已存在'
});
} else {
var hmac = crypto.createHmac('sha256', 'liuyueyi');
var password = hmac.update(ctx.request.body['password']).digest('hex');
var newUser = new Users({
username: ctx.request.body['username'],
password: password
});
await newUser.save();
console.log('注册成功,可以直接登录!');
ctx.session.user = newUser;
await ctx.render('reg', {
title: 'OA-注册',
success: '注册成功,可以直接登录'
});
return ctx.redirect('/login');
}
}
});
当reg页面的表单被POST上来之后,
1、通过ctx取出请求包中表单的username
值,判断其长度是否超过数据库字段的限制,是的话,就重绘该页面,并传入error
字段,写明错误的原因;
2、通过类似的方法判断两次输入的密码是否一致;
3、查询数据库,看是否已经存在该用户名,
var count = await Users.where('username', ctx.request.body['username']).count('username');
4、如果满足注册条件,则将数据组装成一个新的User对象,调用save()
存入数据库。
至此基本就把简单的数据库select
和insert
操作讲完了,由于其他操作我目前的代码还没有涉及到,以后写到了再补充进来。
由于http是无状态协议,为了让用户的访问操作连贯,就加入了cookie和session的机制,我是用了mongoDB作为session存储的载体,koa有比较成熟的解决方案。
const session = require('koa-session-store');
const mongoStore = require('koa-session-mongo');
app.use(convert(session({
store: mongoStore.create({
db: 'oa-session'
})
})));
这样,就算建立好了整个session存储的环境,其中关于mongodb的配置都直接写在了koa-mongo-session包的index.js源码里,
/**
* Default options
*/
var defaultOptions = {
host: '127.0.0.1',
port: 27017,
collection: 'sessions',
auto_reconnect: false,
ssl: false,
expirationTime: 60 * 60 * 24 * 14, // 2 weeks
w: 1
};
如果需要个性化配置,直接在require('koa-session-mongo')
的时候在后面跟上参数即可。
配置好之后,通过ctx.session即可访问或赋值。
比如登陆成功之后,ctx.session.user = username
;
登出之后,ctx.session.user = null
。