程序员

基于Koa和Mysql的Web Server框架

2017-09-17  本文已影响382人  知小酌

最近在给一个自定义的项目写接口,第一次使用node.js实现,翻了几天的书和资料,本来计划使用express + mongodb实现的,后来发现了Koa,good boy,数据库最终选定的还是mysql,老搭子嘛,搭建了一个基于Koa + Mysql框架:

Koa

koa 是由 Express 原班人马打造的,致力于成为一个更小、更富有表现力、更健壮的 Web 框架。使用 koa 编写 web 应用,通过组合不同的 generator,可以免除重复繁琐的回调函数嵌套,并极大地提升错误处理的效率。koa 不在内核方法中绑定任何中间件,它仅仅提供了一个轻量优雅的函数库,使得编写 Web 应用变得得心应手。

总体框架

总体框架

项目主要分为configapp
config:配置文件
app:遵循MVC架构,应用文件

第三方包

第三方包

sequelizenode下的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);

运行

创建用户 响应 登录响应 加载个人信息

至此,基于KoaMysql的Web Server框架搭建完成,并实现了注册、登录、加载用户个人信息功能。

参考资料

项目中主要参考了

总体框架
sequelize文档

部分代码源自廖雪峰的官网
官网
git

代码

本文中的所有代码已经提交到git上了,大家如果喜欢就去git下star下吧。

Koa-Server的代码详见:[https://github.com/vslimit/koa-server.git)

上一篇下一篇

猜你喜欢

热点阅读