基于Koa和Mysql的Web Server框架

最近在給一個自定義的項(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)目主要分為configapp
config:配置文件
app:遵循MVC架構(gòu)窖梁,應(yīng)用文件

第三方包

第三方包

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

創(chuàng)建用戶
響應(yīng)
登錄響應(yīng)
加載個人信息

至此瞬捕,基于KoaMysql的Web Server框架搭建完成,并實(shí)現(xiàn)了注冊舵抹、登錄肪虎、加載用戶個人信息功能。

參考資料

項(xiàng)目中主要參考了

總體框架
sequelize文檔

部分代碼源自廖雪峰的官網(wǎng)
官網(wǎng)
git

代碼

本文中的所有代碼已經(jīng)提交到git上了惧蛹,大家如果喜歡就去git下star下吧扇救。

Koa-Server的代碼詳見:[https://github.com/vslimit/koa-server.git)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市香嗓,隨后出現(xiàn)的幾起案子迅腔,更是在濱河造成了極大的恐慌,老刑警劉巖陶缺,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件钾挟,死亡現(xiàn)場離奇詭異,居然都是意外死亡饱岸,警方通過查閱死者的電腦和手機(jī)掺出,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來苫费,“玉大人汤锨,你說我怎么就攤上這事“倏颍” “怎么了闲礼?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長铐维。 經(jīng)常有香客問我柬泽,道長,這世上最難降的妖魔是什么嫁蛇? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任锨并,我火速辦了婚禮,結(jié)果婚禮上睬棚,老公的妹妹穿的比我還像新娘第煮。我一直安慰自己解幼,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布包警。 她就那樣靜靜地躺著撵摆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪害晦。 梳的紋絲不亂的頭發(fā)上特铝,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天,我揣著相機(jī)與錄音壹瘟,去河邊找鬼苟呐。 笑死,一個胖子當(dāng)著我的面吹牛俐筋,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播严衬,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼澄者,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了请琳?” 一聲冷哼從身側(cè)響起粱挡,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎俄精,沒想到半個月后询筏,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡竖慧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年嫌套,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片圾旨。...
    茶點(diǎn)故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡踱讨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出砍的,到底是詐尸還是另有隱情痹筛,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布廓鞠,位于F島的核電站帚稠,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏床佳。R本人自食惡果不足惜滋早,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望夕土。 院中可真熱鬧馆衔,春花似錦瘟判、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至减细,卻和暖如春匆瓜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背未蝌。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工驮吱, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人萧吠。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓左冬,卻偏偏與公主長得像,于是被迫代替她去往敵國和親纸型。 傳聞我的和親對象是個殘疾皇子拇砰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內(nèi)容