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


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

參考鏈接


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM