天天看点

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就可以访问了