最近在给一个自定义的项目写接口,第一次使用node.js实现,翻了几天的书和资料,本来计划使用express + mongodb实现的,后来发现了Koa,good boy,数据库最终选定的还是mysql,老搭子嘛,搭建了一个基于Koa + Mysql框架:
Koa
koa 是由 Express 原班人马打造的,致力于成为一个更小、更富有表现力、更健壮的 Web 框架。使用 koa 编写 web 应用,通过组合不同的 generator,可以免除重复繁琐的回调函数嵌套,并极大地提升错误处理的效率。koa 不在内核方法中绑定任何中间件,它仅仅提供了一个轻量优雅的函数库,使得编写 Web 应用变得得心应手。
总体框架
项目主要分为config和app:
config:配置文件
app:遵循MVC架构,应用文件
第三方包
sequelize是node下的ORM框架,很好很强大,下面的实例中会进行展示。
主要配置
Server.js
/**
* Created by vslimit on 2017/9/8.
*/
'use strict';
require('dotenv').config();
const Koa =require('koa');
const app = new Koa();
const fs = require('fs');
const join = require('path').join;
const bodyParser = require('koa-bodyparser');
const model = join(__dirname, 'app/model');
var Router = require('koa-router');
var router = new Router();
const rest = require('./config/rest');
const config = require('./config');
const port = process.env.PORT || 3000;
module.exports = app;
app.use(bodyParser());
// app.use(async ctx => {
// ctx.body = ctx.request.body;
// });
app.use(rest.restify());
app.use(router.routes()).use(router.allowedMethods());
fs.readdirSync(model)
.filter(file => ~file.search(/^[^\.].*\.js$/))
.forEach(file => require(join(model, file)));
// let files = fs.readdirSync(model);
require('./config/routes')(router);
listen();
module.exports = model;
function listen() {
if (router.get('env') === 'test') return;
app.listen(port);
console.log('Express app started on port ' + port);
}
开发实例
我们应用此框架开发一个功能注册、登录、加载功能的Restful接口,先看下model
User
/**
* Created by vslimit on 2017/9/10.
*/
const db = require('../util/db');
const crypto = require('crypto');
const uuid = require('node-uuid');
const User = db.defineModel('users', {
name: {
type: db.STRING(),
allowNull: true
},
email: {
type: db.STRING(),
unique: true,
allowNull: true
},
password: db.VIRTUAL(),
mobile: {
type: db.STRING(),
unique: true
},
provider: db.STRING(),
hashed_password: db.STRING(),
salt: db.STRING(),
auth_token: {
type: db.STRING(),
allowNull: true
},
access_token: {
type: db.STRING(),
allowNull: true
}
});
User.beforeValidate(function (user) {
if (user.isNewRecord) {
let salt = this.methods.makeSalt();
user.set('salt', salt);
user.set('hashed_password', this.methods.encryptPassword(user.password, salt));
}
});
User.afterCreate(function (user) {
console.log(JSON.stringify(user));
user.access_token = this.methods.makeAccessToken(user.id);
console.log(user.access_token);
user.save();
});
User.methods = {
authenticate: function (password, salt, hashed_password) {
return this.encryptPassword(password, salt) === hashed_password;
},
/**
* Make salt
*
* @return {String}
* @api public
*/
makeSalt: function () {
return Math.round((new Date().valueOf() * Math.random())) + '';
},
/**
* Encrypt password
*
* @param {String} password
* @return {String}
* @api public
*/
encryptPassword: function (password, salt) {
if (!password) return '';
try {
return crypto
.createHmac('sha1', salt)
.update(password)
.digest('hex');
} catch (err) {
return '';
}
},
makeAccessToken: function (id) {
return crypto
.createHmac('sha1', id.toString())
.update(uuid.v4() + Date.now())
.digest('hex');
},
load: function (condition) {
return User.findOne({where: condition});
},
count: function (condition) {
return User.count({where: condition});
},
};
module.exports = User;
然后是controller
users
/**
* Created by vslimit on 2017/9/12.
*/
'use strict';
const User = require('../model/User');
const ApiResult = require('../../config/rest').APIResult;
/**
* Create user
*/
exports.create = async(ctx, next) => {
let mobile = ctx.request.body.mobile;
let password = ctx.request.body.password;
console.log(mobile);
console.log(password);
if (!mobile || !password) {
ctx.rest(ApiResult("", -102, "手机号或密码不能为空"));
} else {
let count = await User.methods.count({mobile: mobile});
console.log(count);
if (count > 0) {
ctx.rest(ApiResult("", -101, "手机号已存在"));
} else {
let user = await User.create({
mobile: mobile,
password: password,
provider: 'local'
});
ctx.rest(ApiResult(user.access_token));
}
}
};
exports.login = async(ctx, next) => {
let mobile = ctx.request.body.mobile;
let password = ctx.request.body.password;
if (!mobile || !password) {
ctx.rest(ApiResult("", -102, "手机号或密码不能为空"));
} else {
let user = await User.methods.load({mobile: mobile});
if (user) {
if (User.methods.authenticate(password, user.salt, user.hashed_password)) {
ctx.rest(ApiResult({
name: user.name,
mobile: user.mobile,
access_token: user.access_token
}));
} else {
ctx.rest(ApiResult("", -105, "用户密码错误"));
}
} else {
ctx.rest(ApiResult("", -103, "用户不存在"));
}
}
};
exports.load = async(ctx, next) => {
var u = await User.findById(ctx.params.id);
ctx.rest(ApiResult(u));
};
配置route
app.post('/api/users', users.create);
app.post('/api/login', users.login);
app.get('/api/users/:id', users.load);
运行
至此,基于Koa和Mysql的Web Server框架搭建完成,并实现了注册、登录、加载用户个人信息功能。
参考资料
项目中主要参考了
总体框架
sequelize文档
部分代码源自廖雪峰的官网
官网
git
代码
本文中的所有代码已经提交到git上了,大家如果喜欢就去git下star下吧。
Koa-Server的代码详见:[https://github.com/vslimit/koa-server.git)