(黑马程序员)MongoDB + Express + art-template 项目实例-博客管理系统 第五页

2.2、 新增用户

2.2.1、为用户列表页面的新增用户按钮添加链接

打开 views-admin 目录下的 user.art 文件,给新增用户按钮添加连接:

<a href="/admin/user-edit" class="btn btn-primary new">新增用户</a>

2.2.2、添加一个连接对应的路由,在路由处理函数中渲染新增用户模板

打开 route 目录下 admin.js 文件,新增 user-edit 路由:

// 创建用户编辑页面路由
admin.get('/user-edit', require('./admin/user-edit'));

在 route-admin 目录下新建 user-edit.js 文件:

module.exports = (req, res) => {
  res.render('admin/user-edit.art');
}

打开浏览器刷新,点击新增用户,发现:可以跳转到表单页了。

2.2.3、为新增用户表单指定请求地址、请求方式、为表单项添加name属性

打开 views-admin 目录下 user-edit.art 文件:

<form class="form-container" method="post" action="/admin/user-edit">

<input name="username" type="text" class="form-control" placeholder="请输入用户名">

<input name="email" type="email" class="form-control" placeholder="请输入邮箱地址">

<input name="password" type="password" class="form-control" placeholder="请输入密码">

<select name="role" class="form-control">
         <option value="normal">普通用户</option>
         <option value="admin">超级管理员</option>
</select>

<select name="state" class="form-control">
         <option value="0">启用</option>
         <option value="1">禁用</option>
</select>

2.2.4、增加实现添加用户的功能路由

打开 route 目录下 admin.js 文件:

// 创建实现添加用户功能
admin.post('/user-edit', require('./admin/user-edit-fn'));

在 route-admin 目录下新建 user-edit-fn.js 文件:

module.exports = (req, res) => {
  res.send('ok'); 
}

回到浏览器刷新表单页面,点击提交按钮,可以看到:

说明路由创建成功了。

2.2.5、接收到客户端传递过来的请求参数

继续编辑 user-edit-fn.js 文件:

module.exports = (req, res) => {
  // 输出请求参数
  res.send(req.body)
}

 刷新浏览器,随便输入一些信息,点击提交。可以看到:接收到的请求参数

项目包含的知识点:Joi

JavaScript 对象的规则描述语言和验证器。实际上就是验证 JavaScript 格式的。

下载安装:

npm install joi

示例代码:

const Joi = require('joi');
// 定义验证规则
const schema = {
  username: Joi.string().alphanum().min(3).max(30).required().error(new Error('错误信息')),
  password: Joi.string().regex(/^[a-zA-Z0-9](3, 30)$/),
  access_token: [Joi.string(), Joi.number()],
  birthyear: Joi.number().integer().min(1900).max(2013),
  email: Joi.string().email()
};
// 使用规则验证对象(第1个参数:要验证对象,第2个参数:验证的规则)
Joi.validate({ username: 'abc', birthyear: 1994}, schema);

● alphanum() 表示 username 属性只能是字母字符串或者是数字字符串,不能包含特殊字符(比如_、$等)
● required() 表示 username 属性是必选属性
● error() 方法指定错误信息
● integer() 表示必须是整数
● valid() 表示合法值

在命令行工具中输入:npm install joi 安装第三方模块。

例子:新建 joi.js 文件:

// 引入 joi 模块
const Joi = require('joi');

// 定义对象的验证规则
const schema = {
  username = Joi.string().alphanum().min(2).max(5)
};

async function run() {
  try {
    // 实施验证(第1个参数:要验证对象,第2个参数:验证的规则)
    await Joi.validate({ username: 'abc'}, schema);
  } catch (err) {
    console.log(err.message);
  }
  console.log('验证通过');
}
run();

在命令行工具中输入:nodemon joi.js ,可以看到:

把 username 改为 a,在看下结果:报错信息

这个英文的错误提示信息,对用户来说不够友好,我们可以自定义错误信息: 

username: Joi.string().alphanum().min(2).max(5).error(new Error('username 属性没有通过验证'))

回到命令行工具可以看到:

注意:使用 Joi.validate()实施验证,这个方法返回 promise 对象,可以在后面使用 then 或者 catch 捕获错误信息;也可以通过异步函数的方式来验证对象,但是错误信息要通过 try catch 来捕获。

2.2.6、对请求参数的格式进行验证

打开 user-edit-fn.js 文件,引入 joi 模块:

// 引入 joi 模块
const Joi = require('joi');

// 引入 joi 模块
const Joi = require('joi');

module.exports = async (req, res) => {
  // 定义对象的验证规则
  const schema = {
    username: Joi.string().alphanum().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('密码格式不符合验证规则')),
    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 (err) {
    // 验证没有通过
    // 重定向回用户添加页面
    // res.redirect('/admin/user-edit?message=' + err.message);
    res.redirect(`/admin/user-edit?message=${err.message}`);
  }

  res.send(req.body)
}

 打开 route-admin 目录下的 user-edite.js 文件,添加代码:

module.exports = (req, res) => {
  const {message} = req.query;
  return res.render('admin/user-edit.art', {message: message});
}

在打开 views-admin 目录下的 user-edit.art 文件,把错误信息改为 message :

<p class="tips">{
   
   {message}}</p>

回到浏览器,重新登录后添加用户,直接点提交按钮,可以看到错误信息提示:

2.2.7、验证当前要注册的邮箱地址是否已经注册过

打开 route-admin 目录下 user-edit-fn.js 文件,导入用户集合:

// 导入用户集合构造函数
const { User } = require('../../model/user');

// 根据邮箱地址查询用户是否存在
const user = await User.findOne({email: req.body.email})
  // 如果用户已经存在,那么邮箱地址已经被占用
  if (user) {
    return res.redirect(`/admin/user-edit?message=邮箱地址已经被占用`);
}

 回到浏览器重新登录,提交已经存在的用户信息,发现结果:

2.2.8、对密码进行加密处理

打开 route-admin 目录下 user-edit-fn.js 文件,导入 bcrypt 模块:

// 导入 bcrypt 模块
const bcrypt = require('bcrypt');

// 对密码进行加密处理
//生成随机字符串
const salt = await bcrypt.genSalt(10);
// 进行加密
const password = await bcrypt.hash(req.body.password, salt);
req.body.password = password;
res.send(req.body.password);

在浏览器重新提交,可以看到结果:说明密码加密成功

2.2.9、将用户信息添加到数据库中

继续编辑 user-edit-fn.js 文件:

// 将新用户信息添加到数据库中
await User.create(req.body);

2.2.10、重定向页面到用户列表页面

继续编辑 user-edit-fn.js 文件:

// 将页面重定向到用户列表页   
res.redirect('/admin/user');

回到浏览器,重新添加新用户,成功后跳回到用户列表页。打开 Compass 软件,可以看到:

 user-edit-fn.js 文件完整代码:

// 引入 joi 模块
const Joi = require('joi');
// 导入 bcrypt 模块
const bcrypt = require('bcrypt');
// 导入用户集合构造函数
const { User } = require('../../model/user');

module.exports = async (req, res) => {
  // 定义对象的验证规则
  const schema = {
    username: Joi.string().alphanum().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('密码格式不符合验证规则')),
    role: Joi.string().valid('normal', 'admin').required().error(new Error('角色值非法')),
    state: Joi.number().valid(0, 1).required().error(new Error('状态值非法'))
  };

  try {
    // 实施验证(第1个参数:要验证对象,第2个参数:验证的规则)
    await Joi.validate(req.body, schema);
  }catch (err) {
    // 验证没有通过
    // 重定向回用户添加页面
    // res.redirect('/admin/user-edit?message=' + err.message);
    return res.redirect(`/admin/user-edit?message=${err.message}`);
  }

  // 根据邮箱地址查询用户是否存在
  const user = await User.findOne({email: req.body.email})
  // 如果用户已经存在,那么邮箱地址已经被占用
  if (user) {
    return res.redirect(`/admin/user-edit?message=邮箱地址已经被占用`);
  }
  // 对密码进行加密处理
  //生成随机字符串
  const salt = await bcrypt.genSalt(10);
  // 进行加密
  const password = await bcrypt.hash(req.body.password, salt);
  // 替换密码
  req.body.password = password;
  // 将新用户信息添加到数据库中
  await User.create(req.body);
  // 将页面重定向到用户列表页   
  res.redirect('/admin/user');
}

代码优化1:验证用户信息

分离请求参数验证的代码,在 model 目录下 user.js 文件中都是对用户数据操作相关的代码 ,所以可以放在这里。

user.js 文件

// 引入 joi 模块
const Joi = require('joi');

// 验证用户信息
const validateUser = user => {
  // 定义对象的验证规则
  const schema = {
    username: Joi.string().alphanum().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('密码格式不符合验证规则')),
    role: Joi.string().valid('normal', 'admin').required().error(new Error('角色值非法')),
    state: Joi.number().valid(0, 1).required().error(new Error('状态值非法'))
  };

  // 实施验证(第1个参数:要验证对象,第2个参数:验证的规则)
  return Joi.validate(user, schema);
}

// 将用户信息集合作为模块成员进行导出
module.exports = {
  User,
  validateUser
}

回到 user-edit-fn.js 中引用:

// 导入用户集合及验证用户
const { User, validateUser } = require('../../model/user');

module.exports = async (req, res) => {
  try {
    await validateUser(req.body);
  }
  。。。

回到浏览器中验证下,ok,没问题,功能都正常。

代码优化2:错误处理

错误处理中间件是一个集中处理错误的地方。

打开 app.js 文件,定义错误处理中间件,把重定向的代码粘过来:

// 错误处理中间件
app.use((err, req, res, next) => {
  res.redirect(`/admin/user-edit?message=${err.message}`);
})

但是这里的路由和参数不可能写死,所以在 user-edit-fn.js 中添加 next:

module.exports = async (req, res, next) => {
  try {
    await validateUser(req.body);
  }catch (err) {
    // 验证没有通过
    // 重定向回用户添加页面
    // return res.redirect(`/admin/user-edit?message=${err.message}`);
    return next()
  }

next() 只能传递一个参数,并且是字符串类型,而我们需要传递两个参数。所以需要传递一个参数,用对象的形式,然后再转换为字符串类型。JSON.stringify() 将对象数据类型转换为字符串数据类型

所以要修改为:

try {
    await validateUser(req.body);
}catch (err) {
    // 验证没有通过
    // 重定向回用户添加页面
    // return res.redirect(`/admin/user-edit?message=${err.message}`);
    // JSON.stringify() 将对象数据类型转换为字符串数据类型
    return next(JSON.stringify({path: '/admin/user-edit', message: err.message}))
}

回到 app.js 文件修改错误处理中间件代码:

// 错误处理中间件
app.use((err, req, res, next) => {
  // 框字符串类型转换为对象类型 JSON.parse()
  const result = JSON.parse(err);
  res.redirect(`${result.path}?message=${result.message}`);
})

回到浏览器中验证下:错误提示没问题

下面把 user-edit-fn.js 文件中的另一个错误处理信息也修改下: 

if (user) {
    // return res.redirect(`/admin/user-edit?message=邮箱地址已经被占用`);
    return next(JSON.stringify({path: '/admin/user-edit', message: '邮箱地址已经被占用'}))
}

2.3、 用户列表

2.3.1、访问用户列表页时,在对应的路由处理函数中,将所有的用户信息从数据库总查询出来。

打开 route-admin 目录下的 userPage.js 文件:

// 导入用户集合构造函数
const { User } = require('../../model/user');

module.exports = async (req, res) => {
  // 将用户信息从数据库中查询处理
  let users = await User.find({})
  res.render('admin/user.art', { users: users })
}

2.3.2、将查询中来的数据渲染到用户列表中

打开 views-admin 目录下的 user.art 文件:

<tbody>
        {
   
   {each users}}
             <tr>
                  <!-- @表示原文输出 -->
                  <td>{
   
   {@$value._id}}</td> 
                  <td>{
   
   {$value.username}}</td>
                  <td>{
   
   {$value.email}}</td>
                  <td>{
   
   {$value.role == 'admin' ? '超级管理员' : '普通用户'}}</td>
                  <td>{
   
   {$value.state == 0 ? '启用' : '禁用'}}</td>
                  <td>
                      <a href="user-edit.html" class="glyphicon glyphicon-edit"></a>
                      <i class="glyphicon glyphicon-remove" data-toggle="modal" data-target=".confirm-modal"></i>
                  </td>
             </tr>
        {
   
   {/each}}
</tbody>

在浏览器刷新页面,可以看到:用户列表

还有找到1个用户,这里要改为动态数据:

<span>找到{
   
   {users.length}}个用户</span>

刷新浏览器: 

猜你喜欢

转载自blog.csdn.net/weixin_39202130/article/details/119045881