Node Koa2 mysql搭建后端服务器
发布于 4 年前 作者 gangcai 3529 次浏览 来自 分享

前言

前段时间公司要开发自己的一套博客系统,网上找了很多源码,看了下都比较复杂,因为是公司内自己的技术博客,不需要很复杂,索性自己搭一套后端服务,找一套博客前端模版即可实现,话不多说开始搭建历程吧。

开发环境

  • node.js v8.0.0+
  • mysql ^2.1.0
  • koa ^2.7.0
  • redis ^3.0.2
  • sequelize ^5.21.5

准备工作

项目中用到了es6 es7的一些语法 比如promise,async await ,还有熟悉mysql sequlize的一些语法

安装 mysql

到官网 https://www.mysql.com/downloads 下载对应版本,并安装数据库

安装 Sequelize

安装sequlize

npm install sequelize --save

安装 mysql2

npm install mysql2 --save

下载redis

具体操作不一一说明了,网上有教程
https://www.jianshu.com/p/bb7c19c5fc47

目录结构

先上图

  • app/controllers 控制器处理业务逻辑
  • app/models 逻辑实现的方法
  • app/schema 定义sequlize模型,也就是表模型
  • bin/www 项目启动文件
  • config/env 不同开发环境配置文件
  • config/config 读取config/env配置文件
  • config/constants 定义一些枚举
  • config/redis redis配置文件
  • config/secretKeys session redis一些密钥
  • middleware 项目所需中间件
  • models sequlize 初始化连接池文件
  • public 项目静态文件如 js css等
  • routers 路由文件
  • utils 公共工具包或方法
  • views 视图页面
  • .eslintrc.js eslint 检查文件
  • app.js 入口文件

使用 Sequelize 初始化连接池

'use strict';

const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const basename = path.basename(__filename);
const config = require('../config/config');
const db = {};

let sequelize;
sequelize = new Sequelize(config.db.database, config.db.username, config.db.password, config.db);

sequelize.authenticate().then(()=> {
  console.log('db success')
}).catch(() => {
  console.log('db error')
})
sequelize.sync({alter: true})

db.sequelize = sequelize;
db.Sequelize = Sequelize;

module.exports = db;

配置 config

config 通过config 可以读取不同环境的env文件

/**
 * [@description](/user/description) 配置文件
 * [@author](/user/author) Tony
 */

process.env.NODE_CONFIG_DIR = __dirname + '/env';
const config = require('config');

module.exports = config

配置redis

/**
 *  [@description](/user/description) 连接redis的方法
 *  [@author](/user/author) Tony
 */
const redis = require('redis')
const config = require('./config')

const redisClient = redis.createClient(config.redisStore);
redisClient.auth(config.redisStore.pass, function() {
  console.log('Redis client connected');
});
redisClient.on("error", function(err) {
  console.log("Error " + err);
});

/**
 * redis 操作插入
 * [@param](/user/param) {*} key 
 * [@param](/user/param) {*} val 
 * [@param](/user/param) {*} timeout 
 */
function set(key,val,timeout = 60*60) {
    if(typeof val === 'object') {
        val = JSON.stringify(val)
    }
    redisClient.set(key,val)
    redisClient.expire(key,timeout)
}
/**
 * redis 查找
 * [@param](/user/param) {*} key 
 */
function get(key) {
    const promise = new Promise((resolve,reject)=> {
        redisClient.get(key,(err,val)=> {
            if(err) {
                reject(err)
                return
            }
            if(val == null) {
                resolve(null)
                return
            }
            try {
                resolve(JSON.parse(val))
            } catch (error) {
                resolve(val)
            }
        })
    })
    return promise
}

module.exports = {
    redisClient,
    set,
    get
}

配置密钥文件

/**
 * [@description](/user/description) 密钥常量
 * [@author](/user/author) tony
 */

module.exports = {
    CRYPTO_SECRET_KEY: 'xxxxxxxx',
    SESSION_SECRET_KEY: 'xxxxxxxxx'
}

上面都是配置文件,下面开始真正的开发啦

1. 定义数据表模型

/**
 *  定义user schema
 */
module.exports = function(sequelize, DataTypes) {
  return sequelize.define("user", {
      userName: {
        type: DataTypes.STRING,
        allowNull: false,
        comment: '用户名'
      },
      password: {
        type: DataTypes.STRING,
        allowNull: false,
        comment: '密码'
      },
      nickName: {
        type: DataTypes.STRING,
        allowNull: true,
        comment: '昵称'
      }
    {
      freezeTableName: true
    }
  );
};

2. 定义models逻辑用到的方法

const db = require('../../models/index')
const Sequelize = db.sequelize
const Op = db.Sequelize.Op
const User = Sequelize.import('../schema/user')
const { formatUser } = require('./_format')
User.sync({ force: false })

class UserModel{

    /**
     * 获取用户信息
     * [@param](/user/param) {*} userName 
     * [@param](/user/param) {*} password 
     */
    static async getUerInfo(userName,password){
        const whereOpt = {
            userName
        }
        if(password) {
            Object.assign(whereOpt,{ password })
        }
        const result = await User.findOne({
            where: whereOpt,
            attributes: ['id','userName','nickName','picture','city']
        })
        if(result == null) {
            return result
        }
        const formatRes = formatUser(result.dataValues)
        return formatRes
    }
    /**
     * 创建用户
     * [@param](/user/param) {*} data 
     */
    static async createUser(data){
       return await User.create(data)
    }
}

module.exports = UserModel

3. 定义控制器

/**
 *  [@description](/user/description) 用户逻辑处理
 *  [@author](/user/author) Tony
 */

const userModel = require('../models/user')
const { SuccessModel,ErrorModel} = require('../../utils/ResModel')
const doCrypto = require('../../utils/cryp')

class UserControler {

    /**
     * 判断用户是否存在
     * [@param](/user/param) {*} ctx 
     */
    static async isExist(ctx){
        try {
            const { userName } = ctx.request.body
            const userInfo = await userModel.getUerInfo(userName)
            if(userInfo) {
                ctx.body = new SuccessModel(userInfo)
            } else {
                ctx.body = new ErrorModel({
                    errno: 10003,
                    message: '用户名已存在'
                })
            }
        } catch (error) {
            return Promise.reject(error)
        }
    }
    /**
     * 注册
     * [@param](/user/param) {*} ctx 
     */
    static async register(ctx) {
        try {
            const { userName,nickName,password,gender } = ctx.request.body
            const userInfo = await userModel.getUerInfo(userName)
            if(userInfo) {
                ctx.body = new ErrorModel({
                    errno: 10003,
                    message: '用户名已存在'
                })
            }
            const result = await userModel.createUser({ userName, nickName, password : doCrypto(password), gender })
            ctx.body = new SuccessModel(result)
        } catch (error) {
            ctx.body = new ErrorModel({ 
                errno: 10000,
                message: '注册失败'
            })
        }
    }

    /**
     * 登录
     * [@param](/user/param) { } ctx 
     */
    static async login(ctx) {
        const { userName,password } = ctx.request.body
        const userInfo = await userModel.getUerInfo(userName,doCrypto(password))
        if(!userInfo) {
            ctx.body = new ErrorModel({
                errno: 10004,
                message: '用户名或密码不存在'
            })
        }

        if(ctx.session.userInfo == null) {
            ctx.session.userInfo = userInfo
        }

        ctx.body = new SuccessModel(userInfo)
    }
    
}

module.exports = UserControler

4. 定义路由

/**
 * [@description](/user/description) user API 路由
 * [@author](/user/author) Tony
 */

const router = require('koa-router')()
const UserController = require('../../app/controllers/user')
const userValidate = require('../../utils/validator/user')
const { genValidator } = require('../../middleware/validator')

router.prefix('/api/user')

// 用户名是否存在
router.post('/isExist',UserController.isExist)
router.post('/register',genValidator(userValidate),UserController.register)

//登录
router.post('/login',UserController.login)

//返回router
module.exports = router

入口文件

const Koa = require('koa')
const app = new Koa()
const views = require('koa-views')
const json = require('koa-json')
const onerror = require('koa-onerror')
const bodyparser = require('koa-bodyparser')
const logger = require('koa-logger')
const session = require("koa-generic-session")
const redisStore = require('koa-redis')
const jwtKoa = require('koa-jwt')
const config = require('./config/config')
const { SECRET } = require('./config/constants')
const { SESSION_SECRET_KEY } = require('./config/secretKeys')

// 错误处理
onerror(app)

// 中间件
app.use(bodyparser({
  enableTypes:['json', 'form', 'text']
}))
app.use(json())
app.use(logger())
app.use(require('koa-static')(__dirname + '/public'))

app.use(views(__dirname + '/views', {
  extension: 'ejs'
}))

// session 配置
app.keys = [ SESSION_SECRET_KEY ]
app.use(session({
  key: 'koa.sid',                   // cookie name 默认koa.sid
  prefix: 'koa:sess',               // redis key 前缀 默认 koa:sess
  cookie: {
    path: '/',
    httpOnly: true,
    maxAge: 24 * 60 * 60 * 1000  // 单位 ms
  },
  store: redisStore({
    all: `${config.redisStore.host}:${config.redisStore.port}`
  })
}))

// logger
app.use(async (ctx, next) => {
  const start = new Date()
  await next()
  const ms = new Date() - start
  console.log(`${ctx.method} ${ctx.url} - ${ms}ms`)
})

const userViewRouter = require('./routes/view/user')
const userAPIRouter = require('./routes/api/user')

app.use(userViewRouter.routes(), userViewRouter.allowedMethods())
app.use(userAPIRouter.routes(), userAPIRouter.allowedMethods())

// error-handling
app.on('error', (err, ctx) => {
  console.error('server error', err, ctx)
});

module.exports = app

项目启动

npm run start

项目编译

npm run build

接口测试

启动项目后可以通过postman等一些测试工具测试

项目部署

项目可通过nginx pm2等进行部署,具体操作可网上查询,今天先分享到这,一些细节代码没有贴出来,还请见谅。

1 回复

不支持上传文件?

回到顶部