2、项目功能实现
2.1 登录与退出功能
2.1.1、创建用户集合,初始化用户
1)连接数据库
在 model 目录下新建 connect.js 文件:
// 引入 mongoose 第三方模块
const mongoose = require('mongoose');
// 连接数据库
mongoose.connect('mongodb://localhost/blog', { useNewUrlParser: true, useUnifiedTopology: true})
.then(() => console.log('数据库连接成功'))
.catch(() => console.log('数据库连接失败'))
在 app.js 文件中引用:
// 数据库连接
require('./model/connect');
在命令行工具中可以看到:
2)创建用户集合
在 model 目录下新建 user.js:
// 引入 mongoose 第三方模块
const mongoose = require('mongoose');
// 创建用户集合规则
const userSchema = new mongoose.Schema({
username: {
type: String,
required: true,
minlength: 2,
maxlength: 20
},
email:{
type: String,
required: true,
// 唯一性,保证邮箱地址在插入数据库时不重复
unique: true
},
password: {
type: String,
required: true
},
// admin-超级管理员 normal-普通用户
role: {
type: String,
required: true
},
// 0:启用状态 1:禁用状态
state: {
type: Number,
default: 0
}
});
// 创建用户信息集合
const User = mongoose.model('User', userSchema);
// 将用户信息集合作为模块成员进行导出
module.exports = {
User
}
3)初始化用户
在 user.js 文件中创建测试用户:
// 引入 mongoose 第三方模块
const mongoose = require('mongoose');
// 创建用户集合规则
const userSchema = new mongoose.Schema({
username: {
type: String,
required: true,
minlength: 2,
maxlength: 20
},
email:{
type: String,
required: true,
// 唯一性,保证邮箱地址在插入数据库时不重复
unique: true
},
password: {
type: String,
required: true
},
// admin-超级管理员 normal-普通用户
role: {
type: String,
required: true
},
// 0:启用状态 1:禁用状态
state: {
type: Number,
default: 0
}
});
// 创建用户信息集合
const User = mongoose.model('User', userSchema);
User.create({
username: 'itjoe',
email: '[email protected]',
password: '123456',
role: 'admin',
state: 0
}).then(() => {
console.log('用户创建成功')
}).catch(() => {
console.log('用户创建失败')
})
// 将用户信息集合作为模块成员进行导出
module.exports = {
User
}
在 app.js 文件中引用:
// 用户集合
require('./model/user');
这时在命令行工具中可以看到:用户创建成功
打开 Compass 软件,可以看到 blog 数据库中有一个 users 集合:创建用户成功
然后把 app.js 中引入 user.js 的代码删除,并把 user.js 中创建用户部分的代码注释掉。
2.1.2、 为登录表单项设置请求地址、请求方式以及表单项name属性
打开 login.art 文件,添加代码:
<form action="/login" method="post">
<input name="email" type="email" class="form-control" placeholder="请输入邮件地址">
<input name="password" type="password" class="form-control" placeholder="请输入密码">
2.1.3.、当用户点击登录按钮时,客户端验证用户是否填写了登录表单
给表单添加 id:
<form action="/login" method="post" id="loginForm">
在页面下方添加表单的点击事件代码:
<script type="text/javascript">
//为表单添加提交事件
$('#loginForm').on('submit', function () {
// 获取到表单中用户输入的内容,返回值是数组
// [{name: 'email', value:: '用户输入的内容'}]
var f = $(this).serializeArray()
console.log(f)
// 阻止表单默认提交的行为
return false;
});
</script>
打开浏览器刷新页面,随便输入一些内容,可以看到控制台打印出的数组:刚输入的内容
继续修改代码:
<script type="text/javascript">
function serializeToJson(form) {
var result = {};
// serializeArray() 获取到表单中用户输入的内容,返回值是数组
// [{name: 'email', value:: '用户输入的内容'}]
var f = form.serializeArray();
// 把数组转换为对象 {enail: '[email protected]', password: '123456'}
f.forEach(function(item) {
result[item.name] = item.value;
});
return result;
}
//为表单添加提交事件
$('#loginForm').on('submit', function () {
var result = serializeToJson($(this))
console.log(result)
// 阻止表单默认提交的行为
return false;
});
</script>
回到浏览器刷新页面,重新输入内容,可以看到控制台打印出的对象:
在一个真实项目中,对表单进行处理是很常见的操作,所以我们可以把这个方法变为公共的方法。
在 public - admin - js 目录下,新建 common.js 文件,把 serializeToJson 方法剪切过来:
function serializeToJson(form) {
var result = {};
// serializeArray() 获取到表单中用户输入的内容,返回值是数组
// [{name: 'email', value:: '用户输入的内容'}]
var f = form.serializeArray();
// 把数组转换为对象 {enail: '[email protected]', password: '123456'}
f.forEach(function(item) {
result[item.name] = item.value;
});
return result;
}
再在 login.art 文件中引入 common.js 文件:
<script src="/admin/js/common.js"></script>
刷新浏览器冲洗验证下方法是否可以正常使用。
我们还可以再骨架文件中引入 common.js 文件,打开 layout.art 文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<link rel="stylesheet" href="/admin/lib/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="/admin/css/base.css">
{
{block 'link'}} {
{/block}}
</head>
<body>
{
{block 'main'}} {
{/block}}
<script src="/admin/lib/jquery/dist/jquery.min.js"></script>
<script src="/admin/lib/bootstrap/js/bootstrap.min.js"></script>
<script src="/admin/js/common.js"></script>
{
{block 'script'}} {
{/block}}
</body>
</html>
2.1.4.、如果其中一项没有输入,阻止表单提交
继续修改 login.art 文件中的 js 代码:
<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>
浏览器刷新页面,不输入内容就提交的话,会弹出提示信息。
2.1.5、 服务器端接收请求参数,验证用户是否填写了登录表单
把表单的请求地址改为:/admin/login
Express 中接收 post 请求参数需要借助第三方包 body-parser
在命令行工具中下载安装:
npm install body-parser
打开 app.js 文件,引入 body-parser 模块,并进行全局的配置:
// 引入 body-parser 模块,用来处理 post 请求参数
const bodyParser = require('body-parser');
// 配置 body-parser 模块,处理 post 请求参数
app.use(bodyParser.urlencoded({ extended: false }));
extended: false 方法内部使用 querystring 模块处理请求参数的格式
extended: true 方法内部使用第三方模块 qs 处理请求参数的格式
然后重启启动服务器:nodemon app.js
打开 admin.js 文件,添加 /login 的 post 请求:
// 实现登录功能
admin.post('/login', (req, res) => {
// 接收请求参数
res.send(req.body);
});
// 先把请求参数显示到页面中看下效果
下面要对请求参数进行二次验证
2.1.6、 如果其中一项没有输入,为客户端做出响应,阻止程序向下执行
继续编辑下 admin.js 的代码:
// 实现登录功能
admin.post('/login', (req, res) => {
// 接收请求参数
const {email, password} = req.body;
// 如果用户没有输入邮件地址或密码
if (email.trim().length == 0 || password.trim().length == 0) {
return res.status(400).send('<h4>邮件地址或密码错误</h4>')
}
});
我们测试下,先把 login.art 文件下面的 js 代码注释掉,然后刷新浏览器,不填写信息直接提交,可以看到:服务器端的验证
可以把页面美化下,在 views 目录下新建 error.art 文件:
{
{extend './common/layout.art'}}
{
{block 'main'}}
<p class="bg-danger error">{
{msg}}</p>
{
{/block}}
然后修改下 admin.js 文件中的代码:
// 实现登录功能
admin.post('/login', (req, res) => {
// 接收请求参数
const {email, password} = req.body;
// 如果用户没有输入邮件地址或密码
if (email.trim().length == 0 || password.trim().length == 0) {
// return res.status(400).send('<h4>邮件地址或密码错误</h4>')
return res.status(400).render('admin/error.art', {msg: '邮件地址或密码错误'})
}
});
刷新页面,重新提交下看效果。
下面我们在 error.art 文件中设置定时器,3秒后再跳回到 login 登陆页:
{
{extend './common/layout.art'}}
{
{block 'main'}}
<p class="bg-danger error">{
{msg}}</p>
{
{/block}}
{
{block 'script'}}
<script type="text/javascript">
setTimeout(function () {
location.href = '/admin/login';
}, 3000)
</script>
{
{/block}}
回到浏览器中刷新,OK,3秒后跳回到 login 登陆页了。
下面要根据客户端传递过来的邮箱地址,查询用户是否存在。如果用户不存在,则阻止程序继续向下执行,并为客户端做出响应,告知客户端邮箱地址或者密码错误;如果用户存在,则使用客户端传递过来的密码和从数据库中查询出来的用户信息中的密码,进行比对。若两者的密码一致,则表示登录成功;若不一致,则表示登录失败。
2.1.7、根据邮箱地址查询用户信息
继续编辑 admin.js 文件的登录功能代码:
// 导入用户集合构造函数
const { User } = require('../model/user');
// 实现登录功能
admin.post('/login', async (req, res) => {
。。。
// 根据邮箱地址查询用户信息
let user = await User.findOne({email: email.trim()})
});
2.1.8、如果用户不存在,为客户端做出响应,阻止程序向下执行
继续编辑 admin.js 文件的登录功能代码:
// 实现登录功能
admin.post('/login', async (req, res) => {
。。。
// 根据邮箱地址查询用户信息
let user = await User.findOne({email: email.trim()})
// 如果没有查询到用户,user 变量为空
if (user != null) {
// 查询到了用户
} else {
// 没有查询到用户
res.status(400).render('admin/error.art', {msg: '邮件地址或密码错误'})
}
});
2.1.9、如果用户存在,将用户名和密码进行比对
继续编辑 admin.js 文件的登录功能代码:
// 实现登录功能
admin.post('/login', async (req, res) => {
。。。
// 根据邮箱地址查询用户信息
let user = await User.findOne({email: email.trim()})
// 如果没有查询到用户,user 变量为空
if (user != null) {
// 查询到了用户,将客户端传递过来的密码与查询出用户信息中的密码进行比对
if (password == user.password) {
}else{
}
} else {
// 没有查询到用户
res.status(400).render('admin/error.art', {msg: '邮件地址或密码错误'})
}
});
2.1.10、比对成功,用户登录成功
// 查询到了用户,将客户端传递过来的密码与查询出用户信息中的密码进行比对
if (password == user.password) {
//登录成功
res.send('登录成功');
}else{
// 登录失败
}
2.1.11、比对失败,用户登录失败
// 查询到了用户,将客户端传递过来的密码与查询出用户信息中的密码进行比对
if (password == user.password) {
//登录成功
res.send('登录成功');
}else{
// 登录失败
res.status(400).render('admin/error.art', {msg: '邮件地址或密码错误'})
}
刷新浏览器,输入正确的邮件和密码,可以看到: