Bootstrap

Node.js(黑马)笔记05(完结)--博客案例

1、案例初始化
  • 1.建立项目所需文件夹

public 静态资源
model 数据库操作
route 路由
views 模板

  • 2.初始化项目描述文件

npm init -y

  • 3.下载项目所需第三方模块

npm install express mongoose art-template express-art-template

  • 4.创建网站服务器
  • 5.构建模块化路由
  • 6.构建博客管理页面模板

目录结构
在这里插入图片描述
路由admin.js及home.js
home.js

//引入express框架
const express = require('express')

//创建博客展示页面路由
const home=express.Router()

//挂载首页路由
home.get('/',(req,res)=>{
  res.send('欢迎来到博客首页')
})

//将路由对象作为模块成员导出
module.exports=home;

admin.js

//引入express框架
const express = require('express')

//创建博客展示页面路由
const admin=express.Router()

//挂载首页路由
admin.get('/',(req,res)=>{
  res.send('欢迎来到博客管理页')
})

//将路由对象作为模块成员导出
module.exports=admin;

主入口文件app.js

//引入express框架
const express = require('express')

//创建网站服务器
const app=express();

//导入路由对象
const home=require('./route/home')
const admin=require('./route/admin')

//为路由对象匹配请求路径
app.use('/home',home)
app.use('/admin',admin)

//监听端口
app.listen(3000)
console.log('网站服务器已经启动')
2、模板路径是相对根目录/的

模板路径是相对根目录 / 的,因此需要将外联的资源修改成从/ 开始,其他目录层次依次修改。

3、开放静态资源及抽离公共区域代码及使用模板

目录结构
在这里插入图片描述
静态资源文件public目录

common公共代码–header.art头部

<!-- 头部 -->
<div class="header">
  <!-- 网站标志 -->
  <div class="logo fl">
    黑马程序员 <i>ITHEIMA</i>
  </div>
  <!-- /网站标志 -->
  <!-- 用户信息 -->
  <div class="info">
    <div class="profile dropdown fr">
                <span class="btn dropdown-toggle" data-toggle="dropdown">
					admin
					<span class="caret"></span>
                </span>
      <ul class="dropdown-menu">
        <li><a href="user-edit.html">个人资料</a></li>
        <li><a href="#">退出登录</a></li>
      </ul>
    </div>
  </div>
  <!-- /用户信息 -->
</div>
<!-- /头部 -->

common公共代码–aside.art侧边栏

<!-- 侧边栏 -->
<div class="aside fl">
  <ul class="menu list-unstyled">
    <li>
      <a class="item active" href="user.html">
        <span class="glyphicon glyphicon-user"></span>
        用户管理
      </a>
    </li>
    <li>
      <a class="item" href="article.html">
        <span class="glyphicon glyphicon-th-list"></span>
        文章管理
      </a>
    </li>
  </ul>
  <div class="cprt">
    Powered by <a href="http://www.itheima.com/" target="_blank">黑马程序员</a>
  </div>
</div>
<!-- 侧边栏 -->

common公共代码–layout.art主体布局

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <title>Blog - Content Manager</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>
<!--  为主体部分留个坑,将来有页面继承这个模板的时候,应用了main的那部分代码将填入这个坑-->
  {{block 'main'}} {{/block}}

  <script src="/admin/lib/jquery/dist/jquery.min.js"></script>
  <script src="/admin/lib/bootstrap/js/bootstrap.min.js"></script>

<!--为JavaScript留个坑-->
{{block 'script'}} {{/block}}
</body>

</html>

改造各个页面模板,继承公共模板并填坑
/admin/article.art

{{ extend './common/layout.art'}}
{{block 'main'}}
	<!-- 头部 -->
  {{include './common/header.art'}}
    <!-- /头部 -->
    <!-- 主体内容 -->
    <div class="content">
    	<!-- 侧边栏 -->
        {{include './common/aside.art'}}
        <!-- 侧边栏 -->
        <div class="main">
        	<!-- 分类标题 -->
            <div class="title">
                <h4>文章</h4>
                <span>找到1篇文章</span>
                <a href="article-edit.html" class="btn btn-primary new">发布新文章</a>
            </div>
            <!-- /分类标题 -->
            <!-- 内容列表 -->
            <table class="table table-striped table-bordered table-hover custom-table">
                <thead>
                    <tr>
                        <th>ID</th>
                        <th>标题</th>
                        <th>发布时间</th>
                        <th>作者</th>
                        <th>操作</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td>5b9a716cb2d2bf17706bcc0a</td>
                        <td>JavaScript基础一:基本介绍</td>
                        <td>2020-12-23</td>
                        <td>黑马讲师</td>
                        <td>
                            <a href="article-edit.html" class="glyphicon glyphicon-edit"></a>
                            <i class="glyphicon glyphicon-remove" data-toggle="modal" data-target=".confirm-modal"></i>
                        </td>
                    </tr>
                    <tr>
                        <td>5b9a716cb2d2bf17706bcc0a</td>
                        <td>JavaScript基础二:代码书写位置</td>
                        <td>2020-12-24</td>
                        <td>黑马讲师</td>
                        <td>
                            <a href="article-edit.html" class="glyphicon glyphicon-edit"></a>
                            <i class="glyphicon glyphicon-remove" data-toggle="modal" data-target=".confirm-modal"></i>
                        </td>
                    </tr>
                    <tr>
                        <td>5b9a716cb2d2bf17706bcc0a</td>
                        <td>JavaScript基础三:代码注释</td>
                        <td>2020-12-25</td>
                        <td>黑马讲师</td>
                        <td>
                            <a href="article-edit.html" class="glyphicon glyphicon-edit"></a>
                            <i class="glyphicon glyphicon-remove" data-toggle="modal" data-target=".confirm-modal"></i>
                        </td>
                    </tr>
                </tbody>
            </table>
            <!-- /内容列表 -->
            <!-- 分页 -->
            <ul class="pagination">
                <li>
                    <a href="#">
			        <span>&laquo;</span>
			      </a>
                </li>
                <li><a href="#">1</a></li>
                <li><a href="#">2</a></li>
                <li><a href="#">3</a></li>
                <li><a href="#">4</a></li>
                <li><a href="#">5</a></li>
                <li>
                    <a href="#">
			        <span>&raquo;</span>
			      </a>
                </li>
            </ul>
            <!-- /分页 -->
        </div>
    </div>
    <!-- /主体内容 -->
    <!-- 删除确认弹出框 -->
    <div class="modal fade confirm-modal">
        <div class="modal-dialog modal-lg">
            <form class="modal-content">
                <div class="modal-header">
                    <button type="button" class="close" data-dismiss="modal"><span>&times;</span></button>
                    <h4 class="modal-title">请确认</h4>
                </div>
                <div class="modal-body">
                    <p>您确定要删除这篇文章吗?</p>
                </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>
    <!-- /删除确认弹出框 -->
{{/block}}

/admin/article-edit.art

{{ extend './common/layout.art'}}

    {{block 'main'}}
    <!-- 头部 -->
    {{include './common/header.art'}}
    <!-- /头部 -->
    <!-- 主体内容 -->
    <div class="content">
        <!-- 侧边栏 -->
        {{include './common/aside.art'}}
        <!-- 侧边栏 -->
        <div class="main">
            <!-- 分类标题 -->
            <div class="title">
                <h4>5b9a716cb2d2bf17706bcc0a</h4>
            </div>
            <!-- /分类标题 -->
            <form class="form-container">
                <div class="form-group">
                    <label>标题</label>
                    <input type="text" class="form-control" placeholder="请输入文章标题">
                </div>
                <div class="form-group">
                    <label>作者</label>
                    <input type="text" class="form-control" readonly>
                </div>
                <div class="form-group">
                    <label>发布时间</label>
                    <input type="date" class="form-control">
                </div>
                
                <div class="form-group">
                   <label for="exampleInputFile">文章封面</label>
                   <input type="file">
                   <div class="thumbnail-waper">
                       <img class="img-thumbnail" src="">
                   </div>
                </div>
                <div class="form-group">
                    <label>内容</label>
                    <textarea class="form-control" id="editor"></textarea>
                </div>
                <div class="buttons">
                    <input type="submit" class="btn btn-primary">
                </div>
            </form>
            
        </div>
    </div>
    <!-- /主体内容 -->
    {{/block}}
    <!--    填继承模板的坑-->
    {{block 'script'}}
    <script src="/admin/lib/ckeditor5/ckeditor.js"></script>
    <script type="text/javascript">
    
        let editor;

        ClassicEditor
                .create( document.querySelector('#editor'))
                .then(newEditor => {
                    editor = newEditor;
                })
                .catch( error => {
                    console.error( error );
                });

        // 获取数据
        // const editorData = editor.getData();
    </script>
    {{/block}}

/admin/user.art

<!--继承模板-->
{{extend './common/layout.art'}}
{{block 'main'}}
	<!-- 头部 模板引擎来解析,直接写相对路径即可-->
    {{include './common/header.art'}}
    <!-- /头部 -->
    <!-- 主体内容 -->
    <div class="content">
    	<!-- 侧边栏 -->
      {{include './common/aside.art'}}
        <!-- 侧边栏 -->
        <div class="main">
        	<!-- 分类标题 -->
            <div class="title">
                <h4>用户</h4>
                <span>找到1个用户</span>
                <a href="user-edit.html" class="btn btn-primary new">新增用户</a>
            </div>
            <!-- /分类标题 -->
            <!-- 内容列表 -->
            <table class="table table-striped table-bordered table-hover custom-table">
                <thead>
                    <tr>
                        <th>ID</th>
                        <th>用户名</th>
                        <th>邮箱</th>
                        <th>角色</th>
                        <th>状态</th>
                        <th>操作</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td>5b9a716cb2d2bf17706bcc0a</td>
                        <td>wangjian</td>
                        <td>wjb19891223@163.com</td>
                        <td>超级管理员</td>
                        <td>正常</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>
                    <tr>
                        <td>5b9a716cb2d2bf17706bcc0a</td>
                        <td>wangjian</td>
                        <td>wjb19891223@163.com</td>
                        <td>普通用户</td>
                        <td>禁用</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>
                    <tr>
                        <td>5b9a716cb2d2bf17706bcc0a</td>
                        <td>wangjian</td>
                        <td>wjb19891223@163.com</td>
                        <td>普通用户</td>
                        <td>启用</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>
                </tbody>
            </table>
            <!-- /内容列表 -->
            <!-- 分页 -->
            <ul class="pagination">
                <li>
                    <a href="#">
			        <span>&laquo;</span>
			      </a>
                </li>
                <li><a href="#">1</a></li>
                <li><a href="#">2</a></li>
                <li><a href="#">3</a></li>
                <li><a href="#">4</a></li>
                <li><a href="#">5</a></li>
                <li>
                    <a href="#">
			        <span>&raquo;</span>
			      </a>
                </li>
            </ul>
            <!-- /分页 -->
        </div>
    </div>
    <!-- /主体内容 -->
    <!-- 删除确认弹出框 -->
    <div class="modal fade confirm-modal">
        <div class="modal-dialog modal-lg">
            <form class="modal-content">
                <div class="modal-header">
                    <button type="button" class="close" data-dismiss="modal"><span>&times;</span></button>
                    <h4 class="modal-title">请确认</h4>
                </div>
                <div class="modal-body">
                    <p>您确定要删除这个用户吗?</p>
                </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>
    <!-- /删除确认弹出框 -->

{{/block}}

/admin/user-edit.art

{{ extend './common/layout.art'}}
{{block 'main'}}
    <!-- 头部 -->
    {{include './common/header.art'}}
    <!-- /头部 -->
    <!-- 主体内容 -->
    <div class="content">
        <!-- 侧边栏 -->
        {{include './common/aside.art'}}
        <!-- 侧边栏 -->
        <div class="main">
            <!-- 分类标题 -->
            <div class="title">
                <h4>5b9a716cb2d2bf17706bcc0a</h4>
                <p class="tips">错误信息</p>
            </div>
            <!-- /分类标题 -->
            <form class="form-container">
                <div class="form-group">
                    <label>用户名</label>
                    <input type="text" class="form-control" placeholder="请输入用户名">
                </div>
                <div class="form-group">
                    <label>邮箱</label>
                    <input type="email" class="form-control" placeholder="请输入邮箱地址">
                </div>
                <div class="form-group">
                    <label>密码</label>
                    <input type="password" class="form-control" placeholder="请输入密码">
                </div>
                <div class="form-group">
                    <label>角色</label>
                    <select class="form-control">
                        <option>普通用户</option>
                        <option>超级管理员</option>
                    </select>
                </div>
                <div class="form-group">
                    <label>状态</label>
                    <select class="form-control">
                        <option>启用</option>
                        <option>禁用</option>
                    </select>
                </div>
                <div class="buttons">
                    <input type="submit" class="btn btn-primary">
                </div>
            </form>
        </div>
    </div>
    <!-- /主体内容 -->
{{/block}}

主入口文件app.js

//引入express框架
const express = require('express')

//引入path模块
const path=require('path')

//创建网站服务器
const app=express();

//告诉express模板框架所在的位置
app.set('views',path.join(__dirname,'views'))//这里views(第一个)是规定的名字
//告诉express框架模板的默认后缀是什么
app.set('view engine','art')//这里view engine是规定的名字
//当渲染后缀为art的模板时,所使用的模板引擎是什么
app.engine('art',require('express-art-template'))

//静态资源地址
const staticpath=path.join(__dirname,'public')
//开放静态资源
app.use(express.static(staticpath))


//导入路由对象
const home=require('./route/home')
const admin=require('./route/admin')
//为路由对象匹配请求路径
app.use('/home',home)
app.use('/admin',admin)


//监听端口
app.listen(3000)
console.log('网站服务器已经启动')
4、登录功能实现
  1. 创建用户集合,初始化用户
    连接数据库
    创建用户集合
    初始化用户
  2. 为登录表单项设置请求地址、请求方式以及表单项name属性
  3. 当用户点击登录按钮时,客户端验证用户是否填写了登录表单
  4. 如果其中一项没有输入,阻止表单提交
  5. 服务器端接收请求参数,验证用户是否填写了登录表单
  6. 如果其中一项没有输入,为客户端做出响应,阻止程序向下执行

创建model目录存放与服务器连接的相应文件
在这里插入图片描述

user.js

//引入第三方连接数据库模块
const mongoose=require('mongoose')

//创建用户集合规则
const UserSchema=mongoose.Schema({
  username:{
    type:String,
    required:true,
    minlength:2,
    maxlength:20
  },
  email:{
    type:String,
    unique:true,//邮箱及地址不重复
    required: true
  },
  password:{
    type:String,
    required:true
  },
  //超级管理员admin,普通用户normal
  role:{
    type:String,
    required:true
  },
  state:{
    type:Number,
    default:0 //0为启用,1为禁用
  }
})

//创建集合
const User=mongoose.model('User',UserSchema)
/*
//向集合内插入文档
User.create({
  username:'itheima',
  email:'[email protected]',
  password:'123456',
  role:'admin',
  state:0
}).then(()=>{
  console.log('数据插入成功')
}).catch(()=>{
  console.log('用户创建失败')
})

 */

//向外暴露这个集合,将用户集合作为模块成员进行导出
module.exports={
  User,//如果对象键名和键值一致,可以省略掉键值对形式。
};

model/connect.js

//引入第三方连接数据库模块
const mongoose=require('mongoose')

//连接数据库
mongoose.connect('mongodb://localhost/blog',{useNewUrlParser:true})
.then(()=>{console.log('数据库连接成功')})
.catch(()=>{console.log('数据库连接失败')})

/public/admin/js/common.js公共方法

//自定义一个从数组到json的转换函数
function serializeToJson(form){
  //用来接受数组的空对象
  let result={};
  //获取表单中用户输入的内容,返回一个数组
  let f=form.serializeArray();
  //遍历数组等
  f.forEach(function(item){
    result[item.name]=item.value;//name,value是forEach方法默认
  })
  return result
}

重新修改views/admin/common/login.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">
</head>

<body>
    <div class="login-body">
        <div class="login-container">
            <h4 class="title">黑马程序员 - 博客管理员登录</h4>
            <div class="login">
                <form action="/login" method="post" id="loginForm">
                    <div class="form-group">
                        <label>邮件</label>
                        <input name="email" type="email" class="form-control" placeholder="请输入邮件地址">
                    </div>
                    <div class="form-group">
                        <label>密码</label>
                        <input name="password" type="password" class="form-control" placeholder="请输入密码">
                    </div>
                    <button type="submit" class="btn btn-primary">登录</button>
                </form>
            </div>
            <div class="tips"></div>
        </div>
    </div>
    <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>
  <!--表单验证代码-->
    <script type="text/javascript">
      //为表单添加提交事件
      $('#loginForm').on('submit',function(){
        //获取从表单中获取的用户的输入的内容
        let result=serializeToJson($(this))
        //验证表单数据
        //如果用户没有输入邮件地址
        if (result.email.trim().length == 0){
          alert('请输入邮件地址')
          //阻止程序向下执行
          return false;
        }
        //如果用户没有输入邮件地址
        if (result.password.trim().length == 0){
          alert('请输入密码')
          //阻止程序向下执行
          return false;
        }
        console.log(result)

      })
    </script>
</body>

</html>
5、在服务器端验证登录信息
  1. 根据邮箱地址查询用户信息
  2. 如果用户不存在,为客户端做出响应,阻止程序向下执行
  3. 如果用户存在,将用户名和密码进行比对
  4. 比对成功,用户登录成功
  5. 比对失败,用户登录失败
  6. 保存登录状态
  7. 密码加密处理
5.1、使用bcrypt模块进行密码加密

哈希加密是单程加密方式,使用bcrypt模块进行密码加密

  • 导入bcrypt模块
  • 生成随机字符串gen=>generate生成salt盐
  • 使用随机字符串对密码加密

bcrypt依赖的环境
1、python 2.x
2、node-gyp
npm install -g nodey-gyp
3、window-build-tools
npm install --global --production windows-build-tools

	//引入bcrypt加密模块
const bcrypt=require('bcrypt')

//生成随机字符串
//genSalt方法接受一个数值作为参数
//数值越大,生成的随机字符串复杂度越高
//数值越小,生成的随机字符串复杂度越低,默认为10
//返回生成的随机字符串
async function run(password){
  const salt= await bcrypt.genSalt(10)
  //对密码进行加密,第一个参数是要进行加密的明文,第二个参数是随机字符串
  const result= await  bcrypt.hash('123456',salt)
  console.log(result)
}

run()

密码比对
let isEqual =await bcrypt.compare(’明文密码’,‘加密密码’)

5.2、cookie和session

使用expres-session实现session功能
const session=require(‘express-session’)
app.use(session({ secret:‘secret key’}))

安装express-session模块
npm install express-session

5.3、本小段代码

目录结构
在这里插入图片描述
route/admin/login.js

//导入用户集合构造函数,之前在user.js中暴露出来了,但暴露的是一个对象,所以用一个对象来接收
const {User}=require('../../model/user')

//引入bcrypt加密模块
const bcrypt=require('bcrypt')


//login路由的处理函数
const login=async (req,res)=>{
  //接受请求参数,同时请安装body-parser
  const {email,password}=req.body;//对象解构
  if (email.trim().length==0 || password.trim().length==0){
    res.status(400).render('admin/error.art',{msg:'邮件地址或密码错误'})
    return
  }

  //根据邮箱地址查询用户信息,异步执行需要加await拿到异步返回结果,同时函数定义时要加async
  //如果查询到了用户,user对象的值是对象类型
  //如果没有查询到用户,user变量为null
  let user=await User.findOne({email})

  //根据user的类型来判断是否查询到了用户
  if(user){
    //将客户端传递过来的密码和用户信息中的密码进行比对,由于密码已经加密了,所以需要对明文加密
    //true比对成功
    //false比对失败
    let isValid=await bcrypt.compare(password,user.password)
    //如果密码比对成功
    if (isValid){
      //登录成功
      req.session.username=user.username;//将从数据库中查询来的用户名赋值给req的username属性,将用户名存储在请求对象中
      // res.send('登录成功')
      //登录成功后进行重定向到用户列表页面,同时将用户进行存储
      req.app.locals.userInfo=user;
      res.redirect('/admin/user')
    }else{
      //没有查询到用户
      res.status(400).render('admin/error',{msg:'邮箱地址或密码错误'})
    }
  }else {
    //没有查询到用户
    res.status(400).render('admin/error',{msg:'邮箱地址或密码错误'})
  }
}

//暴露导出这个方法
module.exports=login

route/admin/loginOut.js

module.exports=(req,res)=>{
  //删除session
  req.session.destroy(function(){
    //删除cookie
    res.clearCookie('connect.sid');
    //重定向到用户登录页面
    res.redirect('/admin/login')
  })
}

route/admin/loginPage.js

module.exports=(req,res)=>{
  res.render('admin/login')
}

route/adminuserPage.js

module.exports=(req,res)=>{
  res.render('admin/user')
}
6、新增用户
  1. 为用户列表页面的新增用户按钮添加链接
  2. 添加一个连接对应的路由,在路由处理函数中渲染新增用户模板
    3 .为新增用户表单指定请求地址、请求方式、为表单项添加name属性
  3. 增加实现添加用户的功能路由
  4. 接收到客户端传递过来的请求参数
  5. 对请求参数的格式进行验证
  6. 验证当前要注册的邮箱地址是否已经注册过
  7. 对密码进行加密处理
  8. 将用户信息添加到数据库中
  9. 重定向页面到用户列表页面
6.1、Joi格式验证

javascript对象的规则描述语言和验证器
安装joi模块
npm install joi

const joi=require(‘joi’)
//定义验证规则
const schema={
规则内容
}
//将joi模块引入进来
const joi=require('joi')

//引入User集合的构造函数
const {User}=require('../../model/user')

//引入加密模块
const bcrypt=require('bcrypt')

module.exports=async (req,res)=>{
  //定义对象的验证规则
  const schema={
      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('密码不符合要求')),
      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 (e) {
    //验证没通过,要返回到客户端
    //重定向回用户添加页面
    return res.redirect(`/admin/user-edit?message=${e.message}`)
  }

  //根据邮箱地址查询用户是否存在
  let 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')
}
7、formidable

解析表单,支持get请求参数,post请求参数、文件上传。

// 引入formidable模块
 const formidable = require('formidable');
 // 创建表单解析对象
 const form = new formidable.IncomingForm();
 // 设置文件上传路径
 form.uploadDir = "/my/dir";
 // 是否保留表单上传文件的扩展名
 form.keepExtensions = false;
 // 对表单进行解析
 form.parse(req, (err, fields, files) => {
     // fields 存储普通请求参数
         // files 存储上传的文件信息
 });
8、formidable

文件读取 FileReader

var reader = new FileReader();
 reader.readAsDataURL('文件');
 reader.onload = function () {
     console.log(reader.result); 
 }
9、关于populate(‘author’)渲染出差的解决办法

https://blog.csdn.net/qq_38294099/article/details/112762554
解决办法是
let articles=await Article.find().populate(‘author’)后面.lean()

let articles=await Article.find().populate('author').lean()
10、数据分页 mongoose-sex-page
const pagination = require('mongoose-sex-page');
pagination(集合构造函数).page(1) .size(20) .display(8) .exec();

返回的是对象类型

11、为mogodb数据库添加账号
  1. 以系统管理员的方式运行powershell

  2. 连接数据库 mongo

  3. 查看数据库 show dbs

  4. 切换到admin数据库 use admin

  5. 创建超级管理员账户 db.createUser()

  6. 切换到blog数据 use blog

  7. 创建普通账号 db.createUser()

  8. 卸载mongodb服务
    1. 停止服务 net stop mongodb
    2. mongod --remove

  9. 创建mongodb服务
    mongod --logpath=“C:\Program Files\MongoDB\Server\4.1\log\mongod.log” --dbpath=“C:\Program Files\MongoDB\Server\4.1\data” --install –-auth

  10. 启动mongodb服务 net start mongodb

  11. 在项目中使用账号连接数据库
    mongoose.connect(‘mongodb://user:pass@localhost:port/database’)

12、第三方模块config

作用:允许开发人员将不同运行环境下的应用配置信息抽离到单独的文件中,模块内部自动判断当前应用的运行环境,

并读取对应的配置信息,极大提供应用配置信息的维护成本,避免了当运行环境重复的多次切换时,手动到项目代码中修改配置信息

  • 使用npm install config命令下载模块
  • 在项目的根目录下新建config文件夹
  • 在config文件夹下面新建default.json、development.json、production.json文件
  • 在项目中通过require方法,将模块进行导入
  • 使用模块内部提供的get方法获取配置信息
13、第三方模块config将敏感配置信息存储在环境变量中

在config文件夹中建立custom-environment-variables.json文件
配置项属性的值填写系统环境变量的名字
项目运行时config模块查找系统环境变量,并读取其值作为当前配置项属于的值

;