express框架

  1. 使用send()方法代替end
  2. 自动设置http状态码,
  3. 自动检测响应内容的类型,
  4. 设置相应内容类型及其编码

中间件

  1. 一堆方法,中间件方法,请求处理函数
  2. 使用app.use方法,匹配所有的方式
  3. 应用
  • 路由保护
  • 网站维护
  • 自定义404状态码

    查看代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    const express = require('express');
    // 创建网站服务器
    const app = express();

    // app.get('/',(req,res)=>{
    // // 使用send()方法代替end,自动设置http状态码,自动检测响应内容的类型,对那个设置相应内容类型及其编码
    // res.send('启动成功')
    // })
    // app.get('/list',(req,res)=>{
    // res.send({name:'张是',age:1})
    // })

    // 中间件,使用next继续执行
    // 使用app.use方法,匹配所有的请求方式,可以自定义404界面
    app.use('/',(req,res,next)=>{
    console.log(2);
    next()
    })
    app.get('/',(req,res,next)=>{
    req.name = 'zs'
    next()
    })
    app.get('/',(req,res)=>{
    res.send(req.name)
    })
    // 可以自定义404界面
    app.use((req,res,next)=>{
    res.status(404).send('当前访问的页面不存在')
    })
    app.listen(3000)
    console.log('网站服务器启动成功');

错误处理中间件

  1. 统一处理错误 app.use((err,req,res,next)=>{})
  2. 异步代码执行出错无法读取,手动使用next方法

    同步代码

    1
    2
    3
    4
    5
    6
    7
    // 错误处理中间件
    app.get('/index',(req,res)=>{
    throw new Error('程序发生了未知错误')
    })
    app.use((err,req,res,next)=>{
    res.status(500).send(err.message)
    })

    异步代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 错误处理中间件
    app.get('/index',(req,res,next)=>{
    // throw new Error('程序发生了未知错误')
    fs.readFile('./dawdaw','utf8',(err,result)=>{
    if(err!=null){
    next(err)
    }else{
    res.send(result)
    }
    })
    })
    app.use((err,req,res,next)=>{
    res.status(500).send(err.message)
    })

捕获错误

  1. try catch 语句
  2. 捕获异步函数错误

    查看答案

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    const promisify = require('util').promisify
    app.get('/index',async (req,res,next)=>{
    try{
    await readFile('./add.js')
    }catch(ex){
    next(ex)
    }
    })
    app.use((err,req,res,next)=>{
    res.status(500).send(err.message)
    })

构造模块化路由

  1. express.Router() 创建路由对象
  2. 将路由请求路径进行匹配

查看答案

1
2
3
4
5
6
7
8
9
10
// 路由访问二级
const express = require('express')
// 创建网站服务器
const app = express();
const admin = require('./router/admin')
const home = require('./router/home')
app.use('/home',home)
app.use('/admin',admin)
app.listen(3000)
// module.exports = home

get/post参数的获取

  1. req.query 获取get 的请求参数
  2. post的请求参数 使用第三方模块 body-parser req.body获取post的请求参数

express路由参数

  1. :后面写要传递的对象参数 params

    查看答案

    1
    2
    3
    app.get('/index/:id/:name/:age',(req,res)=>{
    res.send(req.params);
    })

静态资源访问

  1. express.static('public')对静态资源进行处理,pubic 表示静态资源所在的路径
  2. 格式 app.use(express,static(path.join(__dirname,'public'))

模板引擎

  1. all-template express-art-template
  2. 渲染某个后缀文件时 使用express-art-tenplate

查看答案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const express = require('express')

const path = require('path')

const app = express();
// 设置模板的渲染后缀
app.engine('art',require('express-art-template'))
// 设置模板的默认路径
app.set('views',path.join(__dirname,'views'))
// 渲染模板时不写后缀,默认的后缀进行拼接
app.set('view engine','art')
// 响应可滑动
app.get('/index',(req,res)=>{
// 渲染模板
res.render('index',{
msg:'message'
})
})
app.listen(3000)

app.locals对象

  1. 使用app.locals方法可以在所有的模板下可以获取到

    查看答案

    1
    2
    3
    4
    5
    6
    7
    8
    9
    app.locals.users = [{
    name:'hello',
    age:13,
    sex:'1'
    },{
    name:'张三',
    age:20,
    sex:0
    }]

注意

  1. 静态资源是由浏览器进行解析,
  2. 子模版的相对路径就是当前文件,因为它是由模板引擎解析的

密码加密 bcypt

  1. 哈希密码 单程解密
  2. bcrypt方法 生成随机字符串,hash方法进行加密
  3. 或者使用crypto实现md5简单加密,可以进行二次加密或者添加字符串进行加密处理

查看答案

1
2
3
4
5
6
7
8
9
10
11
12
13
const md5Crypto=(str)=>{
const hash=crypto.createHash('md5');
hash.update(str);
//加密后是二进制的,不好看,转换成16进制,并且字母大写
return hash.digest("hex").toUpperCase();
}
// 设定一个字符加入md5加密中
mdsecret = 'MaX_daw'

// 建用户集合作为模块成员进行导出,开放对象
module.exports = {
User,md5Crypto,mdsecret
}
  1. 在进行密码比对时可以使用(bcypt.compare(暗文密码,明文密码)

cookie和session

  1. cookie中的数据是以域名的形式进行区分的
  2. cookie中的数据是有过期事件的,会随请求被发送到服务器端
  3. session 对象
  4. 使用redirect方法重定向到用户列表页面
  5. 登录拦截 使用中间件重定向 判断session中是否存在某个属性
  6. 使用clearCookie删除页面已经保存的cookie 然后对页面进行重定向

Joi第三方模块

  1. JavaScript对象的规则描述语言和验证器
  2. 注意joi的版本问题 【no function解决方法
  3. validate方法 为异步函数 ,对用户提交的信息进行判断并返回错误

查看答案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const Joi = require('joi')

const Schema = {
username: Joi.string().min(2).max(10).error(new Error('username属性没有通过验证'))
}

async function valwidate(){
try{
await Joi.validate({username:'a'},Schema)
}catch(e){
console.log(e.message);
return;
}
console.log('验证通过');
}
valwidate()

对页面进行分页

  1. 使用countDocuments({}) 来获取数据库集合的总数
  2. 并且定义一个总的页码
  3. 使用limit限制,使用skip跳过n个数据
  4. -有隐式运算

    查看答案

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <!--
    <% for(var i=1;i<=pages;i++){%>
    <li><a href="/admin/user?page=<%=i%>"><%=i%></a></li>
    <%}%>
    -->
    下一页
    <li style="display:<%= page-0+1 > pages ? 'none' : 'inline' %>">
    <a href="/admin/user?page=<%= page-0+1 %>">
    <span>&raquo;</span>
    </a>
    </li>

标识编码

  1. 使用res.app.locals.currentLink 来标识当前选中的页面
  2. 可以使用js来改变当前的选中情况
  3. 表单enctype属性表示编码
  4. 指定表单数据的编码类型 原型 application/x-www-form-urlencoded
  5. 加个表单对的数据编码成二进制的类型 multipart/form-data

formidable解析表单

  1. 解析表单,支持get请求参数,post请求参数,文件上传
  2. 使用form.keepExtensions 保留表单上传文件的扩展名
  3. form.uploadDir 表示设置文件的上传路径
  4. 可以对表单提交的二进制信息类型进行解析
  5. fields 保存普通的表单信息 files 表示文件的信息

    查看答案

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    const  formidable  = require("formidable")

    // 导入第三方模块 formidable
    const path = require('path')

    module.exports = (req,res) =>{
    // 创建表单解析对象
    const form = new formidable.IncomingForm()
    // 配置上传文件的后缀
    form.uploadDir = path.join(__dirname,'../','../','public','uploads')
    // 保留上传文件的后缀
    form.keepExtensions = true
    // 解析表单
    form.parse(req,(err,fields,files) =>{
    // 1. err表示错误对象,如果表单解析失败 err里面存储错误信息 如果表单解析成功
    // fields表示普通的表单数据
    // files表示保存了和上传文件相关的数据
    res.send(files)
    })
    // res.send('ok')
    }

文件读取

  1. FileReader() readAsDataURL('文件')
  2. 并且readAsDataURL 为异步函数无法直接获取信息,需要调用 onload来返回读取的结果

    查看答案

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    let file = document.querySelector('#file');
    let preview = document.querySelector('#preview')
    file.onchange = function(){
    let reader = new FileReader();
    // 用户选择的文件列表
    reader.readAsDataURL(this.files[0]);
    // 监听onload事件
    reader.onload = function(){
    // 将获取到的文件结果显示在页面中
    preview.src = reader.result
    }
    }

数据分页

  1. 使用第三方模块 mongoose-sex-page
  2. page 表示当前页 size 表示每页显示数据的条数,display表示客户端显示的页码 total数据总数 pages 总页数
  3. $index 表示当前某个标签的索引号
  4. 在进行内容渲染时 使用正则将内容中html标签去除 replace方法/<\/?.+?>/g,并且使用字符串的截取将文本溢出省略 substr(0,150)如果文中出现乱码可以将返回的数据以原文输出方式显示

查看答案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
const {Article} = require('../../model/article')
const pagination = require('mongoose-sex-page')
// const dateFormat = require('dateformat')
module.exports = async (req,res)=>{
res.app.locals.currentLink = 'acticle'
// 获取地址栏中的page页
const { page }= req.query
// 查询数据库中的所有数据,链式查找根据关联的id值查询 使用populate方法查询 括号内添加关联的属性
// 利用 lean() 方法将多级联合的结果转化为普通对象 ,缓解两者的冲突。
// let article = await Article.find().populate('author').lean()
// console.log(article);
// 获取数据集合中的所有数据
let total = await Article.countDocuments({})
// res.send(article)
// 定义每页显示的数据
let pagesize = 5
let pagecount = Math.ceil(total/pagesize)
// page表示获取的是第几页数据
// size表示每一页数据的数量
// display表示页码
// exec()方法避免多级查询与渲染之间发生冲突
let articles = await pagination(Article).find().page(page).size(pagesize).display(pagecount).populate('author').exec()
// 像转换为字符串类型,在转换为对象的类型
let str = JSON.stringify(articles);
let articlejson = JSON.parse(str);
// res.send(articlejson)
res.render('admin/article',{
articles: articlejson,
total:total,
page:page
})
}
  1. 可以使用自定义的参数来进行上下页面的切换,
  • 比如通过对地址栏数据的获取,判断当前页面所对应的页码,然后将此页码进行判断比如进行上一页的切换,如果页面-1小于1则使得当前的地址栏中的页码数等于一,反之使得在当前页码的基础上加一,下一页雷同 注意隐式算法
  • 第二种方法使用css将其隐藏,达到某个页码时将此模块显示
  • 使用第三方模块 pagination-sex-page下的参数执行第一步操作 ,在进行上下页切换时可以使用模块化语言 使用if判断 如果满足条件才让它显示出来

mongoDB数据库添加账号

  1. 查看数据库 连接数据库 mongo 查询数据库 show dbs
  2. 切换到admin数据库 use admin
  3. 创建超级管理员账号 db.createUser({user:'root',pwd:'root',roles:['root']})
  4. 切换到blog数据user blog
  5. 创建普通账号 db.createUser({user:'xxxx',pwd:'xxxx',roles:['readWrite']})
  6. 移除数据库 先停止 net stop mongodb在使用mongod --remove
  7. 创建数据库环境 mongod --logpath="D:\mongoDB\install\server\log\newmongod.log" --dbpath="D:\mongoDB\install\server\data" --install --auth
  8. 连接数据库 使用 mongodb://普通用户名:用户名密码@localhost:端口号/要连接数据库名称
  9. 启动数据库 net start mongodb

开发环境和生产环境

  1. 使用process.env.某个指定的对象来判断当前的环境情况
  2. morgan(‘dev’) 在开发环境中将客户端发送到服务器端的请求信息打印到控制台中,只能在开发环境中使用
  3. production 生产环境

config模块

  1. 将不同运行环境下的应用配置信息抽离到单独的文件中,模块内部字段判断当前的运行环境,并读取对应的配置信息
  2. 创建config文件夹 default development production 三个json文件,通过require的方法导入
  3. get方法,
  4. 这个方法可以自动判断当前的运行环境,并根据相对应的配置信息返回,如果该环境配置下没有匹配到某个指定信息,则在其他的配置信息中自动查询
  5. 将敏感配置信息存储在环境变量中custom-environment-variables.json 自定义环境变量

评论的创建

  1. 创建评论的集合规则 将评论属性的id值与文章的内容id进行关联, 设置其属性为 mongoose.Schema.Types.Objected, ref 为所关联集合名称
  2. 建立评论的路由 只有当用户登录之后才可以使用评论的模块 创建评论的模块
  3. 获取用户在文本框内输入的内容,将此内容通过所在的文章的id值新建一个数据库或者上传到文章的数据库页面
  4. 用户需要登录后才能使用评论 在用户登录后判断如果登录的用户是超级管理员则将页面重定向到博客惯例页面如果是普通用户则将页面重定向到文章的首页
  5. 将登录用户的role存储到session中,在对session进行拦截判断 并且将登录后的首页信息改为用户的信息对评论的状态进行配置,当用户登录后 在本地已经存储到一个userInfo,判断userInfo是否存在,如果存在则将评论模块呈现出来,如果未登录则将评论状态和首页头部的用户状态关闭
  6. 渲染评论页面直接在文章页面渲染
  7. 将用户信息渲染到页面页 创建评论路由 根据评论集合创建规则,上传集合对象 post请求后将页面重定向未当前页面