最近在給一個自定義的項(xiàng)目寫接口铐望,第一次使用node.js實(shí)現(xiàn)冈涧,翻了幾天的書和資料,本來計(jì)劃使用express + mongodb實(shí)現(xiàn)的正蛙,后來發(fā)現(xiàn)了Koa督弓,good boy,數(shù)據(jù)庫最終選定的還是mysql乒验,老搭子嘛愚隧,搭建了一個基于Koa + Mysql框架:
Koa
koa 是由 Express 原班人馬打造的,致力于成為一個更小徊件、更富有表現(xiàn)力奸攻、更健壯的 Web 框架。使用 koa 編寫 web 應(yīng)用虱痕,通過組合不同的 generator睹耐,可以免除重復(fù)繁瑣的回調(diào)函數(shù)嵌套,并極大地提升錯誤處理的效率部翘。koa 不在內(nèi)核方法中綁定任何中間件硝训,它僅僅提供了一個輕量優(yōu)雅的函數(shù)庫,使得編寫 Web 應(yīng)用變得得心應(yīng)手。
總體框架
項(xiàng)目主要分為config和app:
config:配置文件
app:遵循MVC架構(gòu)窖梁,應(yīng)用文件
第三方包
sequelize是node下的ORM框架赘风,很好很強(qiáng)大,下面的實(shí)例中會進(jìn)行展示纵刘。
主要配置
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);
}
開發(fā)實(shí)例
我們應(yīng)用此框架開發(fā)一個功能注冊邀窃、登錄、加載功能的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, "手機(jī)號或密碼不能為空"));
} else {
let count = await User.methods.count({mobile: mobile});
console.log(count);
if (count > 0) {
ctx.rest(ApiResult("", -101, "手機(jī)號已存在"));
} 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, "手機(jī)號或密碼不能為空"));
} 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);
運(yùn)行
至此瞬捕,基于Koa和Mysql的Web Server框架搭建完成,并實(shí)現(xiàn)了注冊舵抹、登錄肪虎、加載用戶個人信息功能。
參考資料
項(xiàng)目中主要參考了
部分代碼源自廖雪峰的官網(wǎng)
官網(wǎng)
git
代碼
本文中的所有代碼已經(jīng)提交到git上了惧蛹,大家如果喜歡就去git下star下吧扇救。
Koa-Server的代碼詳見:[https://github.com/vslimit/koa-server.git)