天天看點

NodeJS Express架構——Mongoose連接配接池、MVC設計

1.Mongoose連接配接池

  • npm install mongoose

  • 在config目錄下建立mongoDB檔案,将資料庫相關的資訊放至這個檔案夾内
    NodeJS Express架構——Mongoose連接配接池、MVC設計
  • 在mongoDB下建立mongo.js
//mongo.js

const mongoose = require('mongoose');
const mongodbConfig = require('./config').mongodb //擷取mongo配置

console.log(mongodbConfig)
 /**
  * 使用 Node 自帶 Promise 代替 mongoose 的 Promise,否則會報錯
  */
mongoose.Promise = global.Promise;

/**
 * 配置 MongoDb options
 */
function getMongodbConfig() {
    let options = {
        useNewUrlParser: true,
        poolSize: 5, // 連接配接池中維護的連接配接數
        reconnectTries: Number.MAX_VALUE,
        keepAlive: 120,
    }
    return options;
}

/**
 * 拼接 MongoDb Uri
 *
 */
function getMongoUrl() {
    let mongoUrl = 'mongodb://';
    let dbName = mongodbConfig.db;
    mongoUrl += `${mongodbConfig.host}:${mongodbConfig.port}`;
    mongoUrl += `/${dbName}`;

    return mongoUrl;
}

/**
 * 建立 Mongo 連接配接,内部維護了一個連接配接池,全局共享
 */
console.log(new Date().getTime())
let mongoClient = mongoose.createConnection(getMongoUrl(), getMongodbConfig());

/**
 * Mongo 連接配接成功回調
 */
mongoClient.on('connected', function() {
    console.log(new Date().getTime())
    console.log('Mongoose連接配接至 :' + getMongoUrl());
});

/**
 * Mongo 連接配接失敗回調
 */
mongoClient.on('error', function(err) {
    console.log('Mongoose 連接配接失敗,原因: ' + err);
});
/**
 * Mongo 關閉連接配接回調
 */
mongoClient.on('disconnected', function() {
    console.log('Mongoose 連接配接關閉');
});

/**
 * 關閉 Mongo 連接配接
 */
function close() {
    mongoClient.close();
}


module.exports = {
    mongoClient: mongoClient,
    close: close,
};
           
  • 在mongoDB下建立config.js,裡面存放我們資料庫的名稱、位址
//config.js

module.exports = {
    "mongodb": {
        "user": "",
        "pass": "",
        "host": "localhost",
        "port": "27017",
        "db": "blog"
    }
}
           

2.Model

  • 在config下建立models,存放我們的資料模型,一個model對應一個dao
Model介紹:
比如我們人類有一雙手,一雙眼睛,一個腦袋,沒有尾巴,這就是模型,Model定義了這個子產品的資料模型。
在代碼中展現為資料管理者,Model負責對資料進行擷取及存放。
資料不可能憑空生成的,要麼是從伺服器上面擷取到的資料,要麼是本地資料庫中的資料,
也有可能是使用者在UI上填寫的表單即将上傳到伺服器上面存放,是以需要有資料來源。
既然Model是資料管理者,則自然由它來負責擷取資料。
Controller不需要關心Model是如何拿到資料的,隻管調用就行了。
資料存放的地方是在Model,而使用資料的地方是在Controller,
是以Model應該提供接口供controller通路其存放的資料(通常通過.h裡面的隻讀屬性)
           
  • 在models下建立user.js,這是使用者表的實體類,我們用mongoose的Schema建立
let { Schema } = require('mongoose');
let { mongoClient } = require('../mongoDB/mongo');

/**
 * 操作 MongoDb 時需要建立兩個檔案 model.js 和 modelDao.js
 *
 * 一. 對于 Model.js 以下幾部分:
 * 1. Schema 必要
 * 2. plugin 可選
 * 3. hook 可選
 * 4. 調用 mongoClient.model() 建立 Model,此處注意,Model 名稱與 js 檔案名一樣,但首字母大寫
 *
 * 二. 對于 modelDao.js
 * 我們需要聲明一個 ModelDao 的 class 繼承自 BaseDao, BaseDao 中包含基本的 crud 操作,也可以根據需求自行定義
 *
 * 三. 外部使用
 * var dao = new ModelDao()
 * dao.crud();
 */

const userSchema = new Schema({
    name: String,
    psw: String, // lowercase 都屬于 setters
}, {
    runSettersOnQuery: true // 查詢時是否執行 setters
});
/**
 * 參數一要求與 Model 名稱一緻
 * 參數二為 Schema
 * 參數三為映射到 MongoDB 的 Collection 名
 */
console.log('實體類',new Date().getTime())
let User = mongoClient.model(`User`, userSchema, 'user');

module.exports = User;
           

3.DAO

  • DAO的概念:
資料通路對象(data access object,DAO)是為某種類型的資料庫或其他持久性機制提供一個抽象接口的對象。
通過映射應用程式對持久層的調用,DAO提供一些特定的資料操作,而無需暴露資料庫細節。
           
  • 在config檔案下建立dao檔案夾,dao檔案夾存放一個模型對應的資料庫操作

由于所有的資料庫操作都是在【增删查改】的基礎上進行操作,是以我們寫了給basedao一個公共js

讓其他的dao繼承這個basedao

  • 在dao檔案下建立basedao.js
//basedao.js
class BaseDao {
    /**
     * 子類構造傳入對應的 Model 類
     *
     * @param Model
     */
    constructor(Model) {
        this.Model = Model;
    }


    /**
     * 使用 Model 的 靜态方法 create() 添加 doc
     *
     * @param obj 構造實體的對象
     * @returns {Promise}
     */
    create(obj) {
        return new Promise((resolve, reject) => {
            let entity = new this.Model(obj);
            this.Model.create(entity, (error, result) => {
                if (error) {
                    console.log('create error--> ', error);
                    reject(error);
                } else {
                    console.log('create result--> ', result);
                    resolve(result)
                }
            });
        });
    }


    /**
     * 使用 Model save() 添加 doc
     *
     * @param obj 構造實體的對象
     * @returns {Promise}
     */
    save(obj) {
        return new Promise((resolve, reject) => {
            let entity = new this.Model(obj);
            entity.save((error, result) => {
                if (error) {
                    console.log('save error--> ', error);
                    reject(error);
                } else {
                    console.log('save result--> ', result);
                    resolve(result)
                }
            });
        });
    }


    /**
     * 查詢所有符合條件 docs
     *
     * @param condition 查找條件
     * @param constraints
     * @returns {Promise}
     */
    findAll(condition, constraints) {
        return new Promise((resolve, reject) => {
            this.Model.find(condition, constraints ? constraints : null, (error, results) => {
                if (error) {
                    console.log('findAll error--> ', error);
                    reject(error);
                } else {
                    console.log('findAll results--> ', results);
                    resolve(results);
                }
            });
        });
    }


    /**
     * 查找符合條件的第一條 doc
     *
     * @param condition
     * @param constraints
     * @returns {Promise}
     */
    findOne(condition, constraints) {
        return new Promise((resolve, reject) => {
            this.Model.findOne(condition, constraints ? constraints : null, (error, results) => {
                if (error) {
                    console.log('findOne error--> ', error);
                    reject(error);
                } else {
                    console.log('findOne results--> ', results);
                    resolve(results);
                }
            });
        });
    }


    /**
     * 查找排序之後的第一條
     *
     * @param condition
     * @param orderColumn
     * @param orderType
     * @returns {Promise}
     */
    findOneByOrder(condition, orderColumn, orderType) {
        return new Promise((resolve, reject) => {
            this.Model.findOne(condition)
                .sort({
                    [orderColumn]: orderType })
                .exec(function(err, record) {
                    console.log(record);
                    if (err) {
                        reject(err);
                    } else {
                        resolve(record);
                    }
                });
        });
    }


    /**
     * 更新 docs
     *
     * @param condition 查找條件
     * @param updater 更新操作
     * @returns {Promise}
     */
    update(condition, updater) {
        return new Promise((resolve, reject) => {
            this.Model.update(condition, updater, (error, results) => {
                if (error) {
                    console.log('update error--> ', error);
                    reject(error);
                } else {
                    console.log('update results--> ', results);
                    resolve(results);
                }
            });
        });
    }


    /**
     * 移除 doc
     *
     * @param condition 查找條件
     * @returns {Promise}
     */
    remove(condition) {
        return new Promise((resolve, reject) => {
            this.Model.remove(condition, (error, result) => {
                if (error) {
                    console.log('remove error--> ', error);
                    reject(error);
                } else {
                    console.log('remove result--> ', result);
                    resolve(result);
                }
            });
        });
    }
}


module.exports = BaseDao;
           
  • 在dao檔案下建立userdao.js用來繼承basedao.js
//userdao.js
const BaseDao = require('./basedao')
const User = require('../models/user')

class UserDao extends BaseDao {
    constructor() {
        super(User)
    }
}
module.exports = UserDao;
           

4.以上就是mongoose連接配接池、MVC模式設計

有了連接配接池、資料操作,接下來就是調用

  • 在routes 下建立 userAPI.js
//userAPI.js
var express = require('express');

const User = require('../config/models/user')
const UserDao = require('../config/dao/userdao');
const JwtUtil = require('../config/jwt/jwt.js'); //token工具
const AesUtil = require('../config/aes/aes.js'); //aes加密

var router = express.Router();
var userdao = new UserDao()
router.post('/login', (req, res) => {
    let name = req.body.name // 前端請求頭用json發送的話用 req.query 或者 req.params
    //接收不到就下載下傳一個body-parse插件來解析
    let psw = req.body.psw
   
    userdao.findOne({ name: name }).then((results) => {
        console.log('findOne dao --> ', results)
        if (results) {
            //解密
            let password = AesUtil.decryption(results.psw)
            console.log('密碼為:' + password)
            if (psw == password) {
                //登入成功,添加token
                let _id = results._id.toString()
                let jwt = new JwtUtil(_id)
                let token = jwt.generateToken()
                res.send(token)//登入成功,傳回token
            } else {
                res.send('密碼錯誤')
            }
        } else {
            res.send('賬号不存在')
        }
    });
})
           

5.API寫好了,在app.js中引入userAPI.js

//app.js
const express = require("express");
const bodyParser = require("body-parser")
const app = express();

// 跨域設定
app.all("*", function(req, res, next) {
    res.header("Access-Control-Allow-Credentials", true);
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "X-Requested-With,Content-Type");
    res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
    res.header("Access-Control-Expose-Headers", "*");
    res.header("Content-Type", "application/json; charset=utf-8");
    next();
});

// 擷取内容
app.use(bodyParser.json())// 解析 application/json
app.use(bodyParser.urlencoded({ extended: true }))// 解析 application/x-www-form-urlencoded
app.use("/user", require("./routes/userAPI.js"));


app.get('/', (req, res) => {
    res.send('api');
});
const port = process.env.PORT || 3001;

app.listen(port, () => {
    console.log('Express server listening on port ' + port);
});

module.exports = app;
           

使用http://localhost:3001/user/login就可以通路了