# 使用 Mongo 进行用户管理
在这个博客应用中,用户和博客之间是一对多的关系
我们使用 mongoose
包来定义用户模型和路由
# 创建用户模型
在 modules/user.js
中定义用户模型
const mongoose = require('mongoose'); | |
const userSchema = new mongoose.Schema({ | |
username: { | |
type: String, | |
required: true, | |
unique: true | |
}, | |
name: String, | |
passwordHash: String, | |
blogs: [ | |
{ | |
type: mongoose.Schema.Types.ObjectId, | |
ref: 'Blog' // 关联到 Blog 模型 | |
} | |
] | |
}) | |
userSchema.set('toJSON', { | |
transform: (document, returnedObject) => { | |
returnedObject.id = returnedObject._id.toString() | |
delete returnedObject._id | |
delete returnedObject.__v | |
delete returnedObject.passwordHash // 不要透露密码哈希 | |
} | |
}) | |
const User = mongoose.model('User', userSchema) | |
module.exports = User |
# 创建用户路由
用户拥有一个唯一的用户名、名字以及一个密码哈希
安装 bcrypt
包生成密码散列
npm install bcrypt |
在 controllers/users.js
中定义用户路由
const bcrypt = require('bcrypt') | |
const usersRouter = require('express').Router() | |
const User = require('../models/user') // 引入 User 模型 | |
usersRouter.post('/', async (request, response) => { | |
const { username, name, password } = request.body | |
const saltRounds = 10 | |
const passwordHash = await bcrypt.hash(password, saltRounds) // 生成密码哈希 | |
const user = new User({ | |
username, | |
name, | |
passwordHash, | |
}) | |
const savedUser = await user.save() // 保存到数据库中 | |
response.status(201).json(savedUser) | |
}) | |
module.exports = usersRouter |
在 app.js
中引入用户路由
const usersRouter = require('./controllers/users') | |
// ... | |
app.use('/api/users', usersRouter) |
# 往错误处理中间件添加违反唯一性错误处理逻辑
const errorHandler = (error, request, response, next) => { | |
if (error.name === 'CastError') { | |
return response.status(400).send({ error: 'malformatted id' }) | |
} else if (error.name === 'ValidationError') { | |
return response.status(400).json({ error: error.message }) | |
} else if (error.name === 'MongoServerError' && error.message.includes('E11000 duplicate key error')) { // 违反唯一性 | |
return response.status(400).json({ error: 'expected `username` to be unique' }) | |
} | |
next(error) | |
} |
# 更改 blog 的路由
在 controllers/blogs.js
中更改路由逻辑
使得当添加博客时,不仅添加到数据库中,而且添加到用户中
const blogsRouter = require('express').Router() | |
const Blog = require('../models/blog') | |
const User = require('../models/user') | |
blogsRouter.get('/', async (request, response) => { | |
const blogs = await Blog.find({}).populate('user') | |
response.json(blogs); | |
}) | |
blogsRouter.post('/', async (request, response) => { | |
if(!request.body.title || !request.body.url) { | |
response.status(400).json({ error: 'title or url missing' }); | |
return | |
} | |
const body = request.body | |
const user = await User.findById(body.userId); // 通过 userId 找到对应的 user | |
const blog = new Blog({ | |
title: request.body.title, | |
author: request.body.author, | |
url: request.body.url, | |
likes: request.body.likes || 0, | |
user: user.id | |
}) | |
const savedBlog = await blog.save(); // 先把新的 blogs 添加到 blogs 数据库中 | |
user.blogs = user.blogs.concat(savedBlog._id); // 更新 user 的 blogs 字段 | |
await user.save(); // 把更新的 user 保存到 user 数据库中 | |
response.status(201).json(savedBlog); | |
}) | |
module.exports = blogsRouter |
# Mongoose 连接查询
Mongoose 的连接查询是通过 .populate()
方法实现的
注意这里的 populate
的参数是各个数据库中需要连接的字段
blogsRouter.get('/', async (request, response) => { | |
const blogs = await Blog.find({}).populate('user') | |
response.json(blogs); | |
}) |