设计一个博客文章管理系统,使之具有管理员登录验证、密码加密、显示、新增、修改、删除、查询用户和文章、数据分页、退出登录等功能。
mongodb + express框架 + node.js
npm init -y
生成 package.json 文件npm install express mongoose art-template express-art-template
// 引入 express 框架
const express = require('express');
// 创建网站服务器
const app = express();
// 监听80端口
app.listen(80);
console.log('服务器已启动');
// 引入 express 框架
const express = require('express');
//创建博客展示页面路由对象
const home = express.Router();
home.get('/home', (req, res) => {
res.send('欢迎来到博客首页');
});
//将路由对象作为模块成员进行导出
module.exports = home;
// 引入 express 框架
const express = require('express');
//创建博客展示页面路由对象
const admin = express.Router();
//创建登录路由
admin.get('/login', require('./admin/loginPage'));
//实现登录功能
admin.post('/login', require('./admin/login'));
//创建用户列表路由
admin.get('/user', require('./admin/userPage'));
//实现退出功能
admin.get('/logout', require('./admin/logout'));
//创建用户编辑页面路由
admin.get('/user-edit', require('./admin/user-edit'));
//创建实现用户添加功能路由
admin.post('/user-edit', require('./admin/user-edit-fn'));
//实现修改用户信息功能
admin.post('/user-modify', require('./admin/user-modify'));
//实现删除用户功能路由
admin.get('/delete', require('./admin/user-delete'));
//将路由对象作为模块成员进行导出
module.exports = admin;
//引入路由模块
const home = require('./route/home');
const admin = require('./route/admin');
//将路由和请求路径进行匹配
app.use('/home', home);
app.use('/admin', admin);
需要注意:
<link href="/admin/css/boot-crm.css" rel="stylesheet" type="text/css" />
{{include './common/header'}}
model/connect.js,代码如下:
//连接数据库
// 引入系统模块mongoose
const mongoose = require('mongoose');
// 数据库连接 27017是mongodb数据库的默认端口
mongoose.connect('mongodb://localhost/blog', { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => console.log('数据库连接成功')) //连接成功
.catch((err) => console.log(err, '数据库连接失败')); //连接失败
model/user.js,代码如下:
const mongoose = require('mongoose');
//创建用户集合规则
const userSchema = new mongoose.Schema({
username: {
type: String,
//true表示title 属性必传;后面的是错误提示内容
required: [true, '请传入用户名'],
//最小长度是2,最大长度是5
minlength: [2, '姓名长度不能小于2'],
maxlength: [20, '姓名长度最大不能超过20']
},
email: {
type: String,
//保证邮箱地址在插入数据库时不重复
unique: true,
required: true
},
password: {
type: String,
required: true
},
role: {
type: String,
required: true
},
//0 启用状态
//1 禁用状态
state: {
type: Number,
default: 0
},
});
//使用规则,创建集合,返回集合构造函数(参数一:集合名称;参数二:集合规则)
const User = mongoose.model('User', userSchema);
//将用户集合作为模块成员进行导出
module.exports = { User };
User.create({
username: 'wxy',
email: '[email protected]',
password: '123456',
role: 'admin',
state: 0 //启用状态
}).then(() => {
console.log('用户创建成功');
}).catch(() => {
console.log('用户创建失败');
})
<form action="/admin/login" method="post" id="loginForm">
<div class="form-group">
<label for="exampleInputEmail1">邮箱:</label>
<input type="text" class="form-control" id="usercode" placeholder="请输入邮箱地址" name="email">
</div>
<div class="form-group">
<label for="exampleInputPassword1">密码:</label>
<input type="password" class="form-control" id="userpassword" placeholder="请输入密码" name="password">
</div>
<div class="form-group">
<div class="checkbox">
<label>
<input type="checkbox">记住密码
</label>
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary" id="btn">立即登录</button>
</form>
<script type="text/javascript">
//为表单添加提交事件
$('#loginForm').on('submit', function() {
//获取到表单中用户输入的内容
var result = serializeToJson($(this));
//如果用户没有输入邮件地址的话
if(result.email.trim().length == 0){
alert('请输入邮件地址');
//阻止程序向下执行
return false;
}
//如果用户没有输入密码
if(result.password.trim().length == 0){
alert('请输入密码');
//阻止程序向下执行
return false;
}
})
</script>
//接收请求参数
//解构 出 email password
const { email, password } = req.body;
//如果用户没有输入邮箱地址
if (email.trim().length == 0 || password.trim().length == 0) {
return res.status(400).render('admin/error', { msg: '邮件地址或者密码错误' });
let user = await User.findOne({ email });
//查询到了用户
if (user) {
// 将客户端传递过来的密码和用户信息中的密码进行比对
if (password == user.password) {
//登陆成功
//将用户名存储在请求对象中
req.session.username = user.username;
req.app.locals.userInfo = user;
//重定向到用户列表页面
res.redirect('/admin/user');
} else {
res.status(400).render('admin/login', { msg: '邮箱地址或密码错误' });
}
} else {
//没有查询到用户
res.status(400).render('admin/login', { msg: '邮箱地址或密码错误' });
}
bcrypt依赖的其他环境
- python 3.8.3
- node-gyp
npm install -g node-gyp- windows-build-tools
npm install – global – production windows-build-tools
cookie: 浏览器在电脑中硬盘中开辟的一块空间,主要供服务器端存储数据。
session: 实际上就是一个对象,存储在服务器端的内存中,在session对象中也可以存储多条数据,每一条数据都有一个sessionid作为唯一标识。
① 建立退出功能的路由。
//实现退出功能
admin.get('/logout', require('./admin/logout'));
② 实现退出功能
module.exports = (req, res) => {
//删除session
req.session.destroy(function() {
//删除cookie
res.clearCookie('connext.sid');
//重定向到用户登录页面
res.redirect('admin/login');
})
}
① 为用户列表页面的新增用户按钮添加链接。
<a href="/admin/user-edit" class="btn btn-primary" >新建用户</a>
② 添加一个链接对应的路由,在路由处理函数中渲染新增用户模板。
//创建用户编辑页面路由
admin.get('/user-edit', require('./admin/user-edit'));
module.exports = async(req, res) => {
//添加操作
res.render('admin/user-edit', {
message: message,
link: '/admin/user-edit',
button: '添加'
});
};
③为新增用户表单制定请求地址、请求方式、为表单项添加name属性。
<form class = "from-container" action="/admin/user-edit" method="post">
<div class="form-group">
<label for="">用户名</label>
<input type="text" class="form-control" placeholder="请填写用户名" name="username">
</div>
<div class="form-group">
<label for="">邮箱</label>
<input type="text" class="form-control" placeholder="请填写邮箱地址" name="email">
</div>
<div class="form-group">
<label for="">密码</label>
<input type="password" class="form-control" placeholder="请输入密码" name="password">
</div>
<div class="form-group">
<label for="">角色</label>
<select name="role" id="normal" class="form-control">
<option value="normal">普通用户</option>
<option value="admin">超级管理员</option>
</select>
</div>
<div class="form-group">
<label for="">状态</label>
<select name="state" id="normal" class="form-control">
<option value="0">启用</option>
<option value="1">禁用</option>
</select>
</div>
<button type="submit" class="btn btn-primary">添加用户</button>
</form>
④ 增加实现添加用户的功能路由。
//创建实现用户添加功能路由
admin.post('/user-edit', require('./admin/user-edit-fn'));
⑤接收到客户端传递过来的请求参数。
res.send(req.body);
⑥ 对请求参数的格式进行验证。
使用 npm install joi
命令下载第三方模块Joi。
//引入joi模块
const Joi = require('joi');
module.exports = async(req, res) => {
//定义对象的验证规则
const schema = {
//必须按字段 要加 required 方法
//.error 自定义错误信息
username: Joi.string().min(2).max(12).required().error(new Error('用户名不符合验证规则')),
email: Joi.string().email().required().error(new Error('邮箱格式不符合验证规则')),
password: Joi.string().regex(/^[a-zA-z0-9]{3,30}$/).required().error(new Error('密码格式不符合验证规则')),
//客户端必须 传入 noemal 或者 admin,传入其他的均是错的
role: Joi.string().valid('normal', 'admin').required().error(new Error('角色不符合验证规则')),
state: Joi.number().valid(0, 1).required().error(new Error('状态值不符合验证规则')),
};
try {
//实施验证
await Joi.validate(req.body, schema);
} catch (error) {
//验证没有通过
//重定向回用户添加页面
res.redirect(`/admin/user-edit?message=${error.message}`);
return;
}
console.log('验证通过');
res.send(req.body);
};
⑦ 验证当前要注册的邮箱地址是否已经注册过。
//根据邮箱地址查询用户是否存在
let user = await User.findOne({ email: req.body.email });
//如果用户已经存在 邮箱地址已经被别人占用
if (user) {
//重定向回用户添加页面
return res.redirect(`/admin/user-edit?message=邮箱地址已经被占用`);
}
⑧ 对密码进行加密处理。
⑨ 优化请求处理代码和错误处理代码,将其放到单独的js文件中。
① 将要修改的用户ID传递到服务器。
<a href="/admin/user-edit?id={{@$value._id}}" class="btn btn-success btn-xs">修改</a>
② 建立用户信息修改功能对应的路由。
//实现修改用户信息功能
admin.post('/user-modify', require('./admin/user-modify'));
③ 接收客户端表单传递过来的请求参数;根据客户端id查询用户信息,并将客户端传递过来的密码和数据库中的密码进行比对;如果比对失败,对客户端做出相应;如果比对成功,将用户信息更新到数据库中。
//导入用户集合的构造函数
const { User } = require('../../model/user');
module.exports = async(req, res) => {
//接收客户端传递过来的请求参数
const { username, email, role, state } = req.body;
//即将要修改的用户id
const id = req.query.id;
//let user = await User.findOne({ _id: id });
//将用户信息更新到数据库中
await User.updateOne({ _id: id }, {
username: username,
email: email,
role: role,
state: state
});
res.redirect('/admin/user');
};
① 在确认删除框中添加隐藏域用以存储要删除用户的ID值。
<!-- 删除确认弹出框 开始-->
<div class="modal fade confire-modal">
<div class="modal-dialog modal-lg">
<form action="" class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span>×</span>
</button>
<h4 class="modal-title">请确认</h4>
</div>
<div class="modal-body">
<p>您确定要删除这个用户吗?</p>
<input type="hidden" name="id">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
<input type="submit" class="btn btn-primary">
</div>
</form>
</div>
</div>
<!-- 删除确认弹出框 结束-->
② 为删除按钮添加自定义属性用以存储要删除用户的ID值。
<a href="#" class="btn btn-danger btn-xs delete" data-id="{{@$value._id}}" data-toggle="modal" data-target=".confire-modal">删除</a>
③ 为删除按钮添加点击事件,在点击事件处理函数中获取自定义属性中的ID值并存储在表单的隐藏域中。
{{block 'script'}}
<!-- 编写js代码 -->
<script type="text/javascript">
//删除用户
$('.delete').on('click', function() {
//获取用户id
var id = $(this).attr('data-id');
alert(id);
//将要删除的用户id存储在隐藏域中
$('#deleteUserId').val(id);
})
</script>
{{/block}} {{/block}}
④ 为删除表单添加提交地址以及提交方式。
<form action="/admin/delete" method="get" class="modal-content">
⑤ 在服务器端建立删除功能路由,接收客户端传递过来的id参数,根据id删除用户。
//导入用户集合的构造函数
const { User } = require('../../model/user');
module.exports = async(req, res) => {
//获取要删除的用户id
const id = req.query.id;
//将用户信息从数据库中删除
await User.findByIdAndDelete({ _id: id });
res.redirect('/admin/user');
};
① 创建数据分页路由(相关代码放在展示页面路由中)。
//导入用户集合的构造函数
const { User } = require('../../model/user');
module.exports = async(req, res) => {
//接收客户端传递过来的当前页参数
let page = req.query.page || 1;
//每一页显示的数据条数
let pagesize = 10;
//查询用户数据的总数
let count = await User.countDocuments({});
//总页数
let total = Math.ceil(count / pagesize);
//页码对应的数据查询开始位置
let start = (page - 1) * pagesize;
//将用户信息从数据库中查询出来
let users = await User.find({}).limit(pagesize).skip(start);
//渲染用户列表模板
res.render('admin/user', {
users: users,
page: page,
total: total
});
};
② user.art 模板中对应的分页代码:
<!--分页开始-->
<nav aria-label="Page navigation">
<ul class="pagination">
<!-- 上一页 -->
<li style="display:<%= page-0-1 < 1 ? 'none' : 'inline' %>">
<a href="/admin/user?page=<%=page-1 %>" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
<% for (var i = 1;i <= total;i++){ %>
<li><a href="/admin/user?page=<%=i %>">{{i}}</a></li>
<% } %>
<!-- 下一页 -->
<li style="display:<%= page-0+1 > total ? 'none' : 'inline' %>">
<a href="/admin/user?page=<%=page-0+1 %>" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
<!--分页结束-->
修改用户
将用户名 哈利波特->哈利波特123;邮箱[email protected]>[email protected];超级管理员->普通用户。