作者:Shailesh Shekhawat
翻译:疯狂的技术宅
原文:https://www.freecodecamp.org/…
未经允许严禁转载
本教程需要事先了解 mongoose 对象关系映射(ORM)技术
介绍
随着程序的增长,日志记录成为跟踪所有内容的关键部分。它对于调试目的尤为重要。
现在已经有了 npm 的日志记录模块。这些模块可以将日志存储在不同格式或级别的文件中。我们将使用流行的 ORM Mongoose 讨论 Node.js Express 程序中的 API 日志记录。
那么如何创建一个 Mongoose 插件,以更清洁的方式为你进行记录并简化 API 日志?
Mongoose 中的插件是什么?
在 Mongoose 中,模式是可插入的。插件就像一个函数,你可以在模式中使用它,并在模式实例上一次次地重用。
Mongoose 还提供 全局插件 ,你可以将其用于所有模式。例如我们将会编写一个插件,它将创建两个 jsons
的diff
并写入 mongodb
。
步骤 1:创建基本日志模式模型
让我们创建一个具有以下六个属性的基本日志模式:
-
Action: 按照它的名称,这是 API 的一个动作过程,无论是
create
、update
、delete
还是别的什么。 - Category: API 类别。例如医生和患者。它更像是一个阶级。
- CreatedBy:正在使用或调用 API 的用户。
- Message: 你可以在此处包含你想要显示的任何类型的消息,这些消息在调试过程中有意义或有帮助。
- Diff: 这是主要属性,它是两个 JSON 的 diff
如果你希望对自己的应用程序有意义,可以添加更多字段,也可以根据需要更改和升级架构。
这是我们的模型:models/log.js
const mongoose = require('mongoose')
const Schema = mongoose.Schema
const {ObjectId} = Schema
const LogSchema = new Schema({action: { type: String, required: true},
category: {type: String, required: true},
createdBy: {type: ObjectId, ref: 'Account', required: true},
message: {type: String, required: true},
diff: {type: Schema.Types.Mixed},
},{timestamps: { createdAt: 'createdAt', updatedAt: 'updatedAt'},
})
LogSchema.index({action: 1, category: 1})
module.exports = mongoose.model('Log', LogSchema)
步骤 2:编写一个函数来获得 2 个 JSON 之间的差异
所以下一步是你需要一个可重用的函数,它将动态创建两个 JSON 的 diff
。
我们称之为 diff.js
const _ = require('lodash')
exports.getDiff = (curr, prev) => {function changes(object, base) {return _.transform(object, (result, value, key) => {if (!_.isEqual(value, base[key]))
result[key] = (_.isObject(value) && _.isObject(base[key])) ? changes(value, base[key]) : value
})
}
return changes(curr, prev)
}
我使用了 lodash
,这是一个提供相同功能的受欢迎的库。
让我们分解上面的函数,看看发生了什么:
-
_.transform: 它是数组
.reduce
的替代品。它会迭代你对象的keys
和values
。它提供了一个accumulator
,是第一个参数。result
是累加器,是可变的。 - _.isEqual: 在两个值之间进行深度比较,以确定它们是否相等。
isEqual:此方法支持比较数组、数组缓冲区、布尔值、日期对象、错误对象、映射、数字、对象、正则表达式、集合、字符串、符号和类型化数组。对象通过它们自己的方法比较,而不是通过继承的、可枚举的属性进行比较。函数和 DOM 节点则进行严格相等的比较,即使用
===
。
这里我们迭代每个对象的属性和值,并将它与旧对象进行比较。
如果当前对象的 value
不等于前一个对象中相同属性的值:base[key]
如果该值是对象本身,我们递归调用函数changes
直到它得到一个值,它最终将作为 result[key]=value
存储在 result
中。
步骤 3:创建一个插件用来 diff 并将其保存到数据库
现在我们需要跟踪数据库中的前一个 document
并在保存到 mongodb
之前创建一个 diff
。
const _ = require('lodash')
const LogSchema = require('../models/log')
const {getDiff} = require('../utils/diff')
const plugin = function (schema) {
schema.post('init', doc => {doc._original = doc.toObject({transform: false})
})
schema.pre('save', function (next) {if (this.isNew) {next()
}else {this._diff = getDiff(this, this._original)
next()}
})
schema.methods.log = function (data) {
data.diff = {
before: this._original,
after: this._diff,
}
return LogSchema.create(data)
}
}
module.exports = plugin
在 Mongoose 中,有不同的钩子可用。现在我们需要使用架构上可用的 init
和 save
方法。
this.isNew()
:如果你正在创建新文档,那么只需返回 next()
中间件。
在 schema.post('init')
的 toObject()
中:
doc._original = doc.toObject({transform: false})
Mongoose Model
继承自 document
,它有一个 toObject()
方法。它将 document
转换为 Object()
和 transform:false
是为了不允许转换返回对象。
步骤 4:用法 – 如何在 express.js API 中使用
在你的主 server.js
或app.js
中:
初始化全局 plugin 以便它可用于所有模式。你还可以通过在架构模型中初始化它来将其用于特定架构。
const mongoose = require('mongoose')
mongoose.plugin(require('./app/utils/diff-plugin'))
这是 user
更新 API 的基本示例:
const User = require('../models/user')
exports.updateUser = (req, res, next) => {return User.findById(req.params.id)
.then(user => {if (!user)
throw new Error('Target user does not exist. Failed to update.')
const {name} = req.body
if (name) user.name = name
return user.save()})
.then(result => {res.json(result)
return result
})
.catch(next)
.then(user => {if (user && typeof user.log === 'function') {
const data = {
action: 'update-user',
category: 'users',
createdBy: req.user.id,
message: 'Updated user name',
}
return user.log(data)
}
}).catch(err => {console.log('Caught error while logging:', err)
})
}
结论
在本教程中,你学习了如何创建 Mongoose 插件并用它来记录 API 中的 changes
。你可以使用插件执行更多操作来构建健壮的 Node.js 程序。
本文首发微信公众号:前端先锋
欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章
欢迎继续阅读本专栏其它高赞文章:
- 深入理解 Shadow DOM v1
- 一步步教你用 WebVR 实现虚拟现实游戏
- 13 个帮你提高开发效率的现代 CSS 框架
- 快速上手 BootstrapVue
- JavaScript 引擎是如何工作的?从调用栈到 Promise 你需要知道的一切
- WebSocket 实战:在 Node 和 React 之间进行实时通信
- 关于 Git 的 20 个面试题
- 深入解析 Node.js 的 console.log
- Node.js 究竟是什么?
- 30 分钟用 Node.js 构建一个 API 服务器
- Javascript 的对象拷贝
- 程序员 30 岁前月薪达不到 30K,该何去何从
- 14 个最好的 JavaScript 数据可视化库
- 8 个给前端的顶级 VS Code 扩展插件
- Node.js 多线程完全指南
- 把 HTML 转成 PDF 的 4 个方案及实现
- 更多文章 …