当前位置 博文首页 > hellojinjin123:koa2+koa-generator+mysql快速搭建nodejs服务器

    hellojinjin123:koa2+koa-generator+mysql快速搭建nodejs服务器

    作者:hellojinjin123 时间:2021-02-02 12:22

    koa2+koa-generator+mysql快速搭建nodejs服务器

    用koa的脚手架koa-generator可以快速生成项目骨架,可以用于发开或者测试接口
    https://github.com/hellojinjin123/node-koa2-template

    1. 全局安装koa-generator(不用全局安装koa)

    项目名字fast-koa

    npm install koa-generator -g
    koa2 fast-koa
    cd fast-koa
    npm install
    

    目录结构如下
    -bin // www 项目启动目录 node ./www
    -public // 静态网站放置目录 也就是vue dist代码放置的地 项目入口index.html
    -routes // 路由
    -views // 视图 服务器渲染使用的模板
    -app.js // 项目入口
    -packaga.json

    2. 启动项目

    // package.json
     "scripts": {
        "start": "node bin/www",
        "dev": "./node_modules/.bin/nodemon bin/www",
        "prd": "pm2 start bin/www",
        "test": "echo \"Error: no test specified\" && exit 1"
      }
    

    运行npm run dev开启服务器
    同时可以看到generator自带了nodemon(Nodemon 是一款非常实用的工具,用来监控你 node.js 源代码的任何变化和自动重启你的服务器)
    如下图:服务器启动了
    avatar1

    3. 项目入口app.js

    // app.js
    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 index = require('./routes/index')
    const users = require('./routes/users')
    
    // error handler
    onerror(app)
    
    // middlewares
    app.use(bodyparser({
      enableTypes:['json', 'form', 'text']
    }))
    app.use(json())
    app.use(logger())
    app.use(require('koa-static')(path.resolve(__dirname, config.publicPath))))
    
    app.use(views(__dirname + '/views', {
      extension: 'pug'
    }))
    
    // 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`)
    })
    
    // routes
    app.use(index.routes(), index.allowedMethods())
    app.use(users.routes(), users.allowedMethods())
    
    // error-handling
    app.on('error', (err, ctx) => {
      console.error('server error', err, ctx)
    });
    
    module.exports = app
    
    

    可以在根目录路添加config.js 把一些公共的配置放入 比如数据库信息,端口,静态资源路径等

    // config.js
    const path = require('path');
    const config = {
        // 项目启动监听的端口
        port: 3000,
    
        publicPath: 'public',
        logPath: 'logs/koa-template.log',
    
        // 数据库配置
        database: {
            HOST: 'xxx',    // 数据库地址
            USERNAME: 'xxx',    // 用户名
            PASSWORD: 'xxx',    // 用户密码
            DATABASE: 'xxx',    // 数据库名
            PORT: 3306      // 数据库端口(默认: 3306)
        }
    };
    
    module.exports = config;
    

    4. koa-static 静态资源中间件

    app.use(require('koa-static')(path.resolve(__dirname, config.publicPath))))
    koa-generator已经配置了静态资源中间件,只要放入public目录,静态网站就可以运行
    浏览http://localhost:3000/,服务器会优先读取public下的index.html
    如果没有index.html,服务器会根据路由,判断'/'是否有内容返回,没有对应路由则返回404 not found
    因为koa-generator默认设置了路由,所以服务器运行返回了Hello Koa 2!
    如下:

    router.get('/', async (ctx, next) => {
      await ctx.render('index', {
        title: 'Hello Koa 2!'
      })
    })
    

    5. 添加models目录

    相当于服务器数据层,存放数据库模型(相当于建表),使用Sequelize进行mysql操作

    const { sequelize, Sequelize } = require('../config/db')
    const { DataTypes, Model } = Sequelize
    
    class Admin extends Model {
    
        /**
         * @description: 添加管理员
         * @param {*} username
         * @param {*} password
         * @return {*} 返回添加的数据
         */
        static async createAdmin({ username, password }) {
            return await this.create({
                username,
                password
            })
        }
    
        /**
         * @description: 根据id修改管理员密码
         * @param {*} id
         * @return {*}  返回修改的数据
         */    
        static async updatepwdById({id, password}) {
            return await this.update({ password }, {
                where: {
                    id
                }
            })
        }
    
        /**
         * @description: 根据id删除管理员
         * @param {*} id
         * @return {*} 
         */    
        static async deleteById(id){
            return await this.destroy({
                where: {
                    id
                }
            })
        }
    
    }
    // 初始化表结构
    Admin.init(
        {
            id: {
                type: DataTypes.INTEGER,
                allowNull: false, //非空
                autoIncrement: true, //自动递增
                primaryKey: true //主键
            },
            username: {
                type: DataTypes.STRING,
                field: "username",
                allowNull: false,
                unique: true   // 唯一约束 用户名不能重复
            },
            password: {
                type: DataTypes.STRING,
                allowNull: false
            },
            active: {
                type: DataTypes.BOOLEAN,
                allowNull: false,
                defaultValue: true
            }
        }, {
        underscored: true, //额外字段以下划线来分割
        timestamps: true, //取消默认生成的createdAt、updatedAt字段
        createdAt: "created_at",
        updatedAt: "updated_at",
        freezeTableName: true, // Model 对应的表名将与model名相同
        comment: "管理员表类",
        // paranoid: true      //虚拟删除
        sequelize, // 我们需要传递连接实例
        // modelName: 'Admin', // 我们需要选择模型名称
        // tableName: 'Admin'  // 表名
    }
    )
    
        // 创建表格
        ; (async () => {
            await Admin.sync();
            console.log("Admin表刚刚(重新)创建!");
            // 这里是代码
        })()
    // 定义的模型是类本身
    // console.log(User === sequelize.models.User); // true
    module.exports = Admin
    
    

    6. mysql数据库的使用(Sequelize stars 23.6k in github )

    Sequelize 是一个基于 promise 的 Node.js ORM, 目前支持 Postgres, MySQL, MariaDB, SQLite 以及 Microsoft SQL Server. 它具有强大的事务支持, 关联关系, 预读和延迟加载,读取复制等功能。

    Sequelize 遵从 语义版本控制。 支持 Node v10 及更高版本以便使用 ES6 功能。https://www.sequelize.com.cn/core-concepts/model-basics

    安装mysql&sequelize

    npm install --save mysql mysql2
    npm install --save sequelize
    
    

    安装sequelize之后,在config目录下创建db.js,进行数据库连接设置

    const Sequelize = require('sequelize');
    const db = require('./index').db
    
    // 初始化数据库
    const sequelize = new Sequelize(db.database, db.username, db.password, {
        host: db.host,
        dialect: 'mysql',
        pool: {
            max: 5,
            min: 0,
            idle: 10000
        }
    })
    
    //测试数据库链接
    sequelize.authenticate().then(function() {
        console.log("数据库连接成功");
    }).catch(function(err) {
        //数据库连接失败时打印输出
        console.error(err);
        throw err;
    });             
    
    module.exports = { sequelize, Sequelize }
    

    7. 添加controllers目录

    有了模型层对操作数据库的支持,就可以进行业务操作了,也就是控制器目录(在这个层可以放心调用models层方法进行curd)

    // 导入模型
    const Admin = require('../models/admin')
    
    module.exports = {
    
        async getAllAdmins(ctx, next) {
            try {
                let data = await Admin.findAll()
                ctx.body = { msg: 1001, data }
            } catch (err) {
                ctx.body = { code: -1, msg: 1000, }
            }
            await next();
        },
    
        async createAdmin(ctx, next) {
            let req = ctx.request.body
            if (!req.username || !req.password) {
                ctx.body = { code: -1, msg: 1002 }
                return await next();
            }
            try {
                let data = await Admin.createAdmin(req)
                ctx.body = { msg: 1003, data }
            } catch (err) {
                ctx.body = { code: -1, msg: 1000 }
            }
            await next();
        },
    
        async updatepwdById(ctx, next) {
            let req = ctx.request.body
            if (req.id && req.password) {
                try {
                    await Admin.updatepwdById(req)
                    ctx.body = { msg: 1004 }
                } catch (err) {
                    ctx.body = { code: -1, msg: 1000 }
                }
            } else {
                ctx.body = { code: -1, msg: 1002 }
            }
            await next();
        },
    
        async deleteById(ctx, next) {
            let query = ctx.request.query // 获取get请求参数
            if (query && query.id) {
                try {
                    await Admin.deleteById(query.id)
                    ctx.body = { msg: 1005 }
                } catch (err) {
                    ctx.body = { code: -1, msg: 1000 }
                }
            } else {
                ctx.body = { code: -1, msg: 1002 }
            }
            await next();
        }
    }
    

    8. 路由配置

    // app.js 中添加
    // routes
    const admin = require('./routes/admin')
    app.use(admin.routes(), admin.allowedMethods())
    

    到此为止,一个完整的请求(接口)就处理完成了
    比如请求 http://localhost:3000/admin/getAllAdmins

    koa经历的简单过程:

    1. 浏览器发出请求 -> 中间件 ->路由中间件 -> 中间件 -> 中间件若干回调 -> 浏览器收到响应
    2. 路由:
    • router.get/post -> controllers.func -> models.func-> mysql
    •  请求行为      ->       业务逻辑     ->   模型支持   ->  入库
      

    9. 配置自定义的中间件处理日志和response消息

    可以参考github代码

    10. 附上一个很好理解的中间件原理的简析

    // 根目录下test.js
    // koa2 中间件原理简析
    // 中间件的仓库
    const arr = [
        async (next) => {
            console.log(1);
            await next();
            console.log(2);
        },
        async (next) => {
            console.log(3);
            await new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve(
                        console.log(4)
                    );
                }, 3000);
            }); // 异步操作 await 会等待后面的promise resolve 后再向下执行
            await next();
            console.log(5);
        },
        async (next) => {
            console.log(6);
        },
        async (next) => {
            // 不会执行 因为上一个函数中没有执行next
            console.log(7);
            await next();
            console.log(8);
        },
        async (next) => {
            // 不会执行 因为前面的函数中没有执行next
            console.log(9);
        }
    ];
    
    function fun(arr) {
        function dispose(index) {
            const currentFun = arr[index];
            const next = dispose.bind(null, index + 1);
            return currentFun(next); // 尾递归
        }
    
        dispose(0);
    }
    
    fun(arr); // 先打印 1 3 一秒后打印4 6 5 2
    

    参考链接

    • https://www.sequelize.com.cn/
    • https://www.cnblogs.com/zjknb/p/12149420.html
    bk