# express boke **Repository Path**: liu-qi2/express-boke ## Basic Information - **Project Name**: express boke - **Description**: 博客项目 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-03-22 - **Last Updated**: 2021-05-17 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # express boke #### 介绍 博客项目 #### 软件架构 express架构 ### express环境的搭建 1、下载并引用express,art-template,express-template,body-parser 2、const app = express();创建服务器 3、创建一二级路由模块 ### 4、配置template //告诉express使用什么模板引擎,第一个参数后缀,第二个使用的模板引擎 app.engine('html',require('express-art-template')); //模板文件存放位置,第一个参数固定的,第二个是文件夹路径 app.set('views',path.join(__dirname,'views')) //默认后缀 app.set('view engine','html') 5、配置body-parser模块 app.use(bp.urlencoded({ extended: false })); 6、开放静态资源 app.use(express.static(path.join(__dirname, 'public'))); 7、创建子路由并引入 const admin = express.Router(); const admin = require('./route/admin.js'); app.use('/admin', admin); ### 抽离公共模块和页面骨架 1、{{include './common/header.html'}} 2、创建骨架文件,并通过{{block 'main}}{{/block}}挖好坑 3、删除页面公共模块和骨架,只保留主体内容。通过{{extend './layout.art'}}引入,并通过block填坑 ### 项目功能的实现 ## 登录 1、创建用户集合,初始化用户 1、连接数据库 connect.js 2、创建用户集合 user.js 3、初始化用户 2、为登录表单项设置请求地址、请求方式以及表单项name属性 3、当客户点击登录按钮时,客户端验证用户是否填写了登录表单 4、如果其中一项没有输入,阻止表单提交 5、服务器端接收请求参数,验证用户是否填写了登录表单 6、如果其中一项没有输入,为客户端作出响应,阻止程序向下进行 7、根据邮箱地址查询用户信息 8、如果用户不存在,为客户端做出响应,阻止程序向下执行 9、如果用户存在,将用户名和密码进行比对 通过bcrypt进行加密验证 10、比对成功,用户登录成功 通过express-session记录用户登录状态,通过app.use拦截请求,判断session.username是否存在来判断是否登录 11、比对失败,用户登录失败 12、实现退出功能 admin.get('/logout', (req, res) => { // 删除session req.session.destroy(function(){ // 删除cookie res.clearCookie('connect.sid'); // 重定向 res.redirect('/admin/login'); }) }) ## 密码加密 bcrypt 哈希密码是单程加密方式,只能加密,不能解密 在加密密码中加入随机字符串可以增加密码被破解的难度 1、导入bcrypt const bcrypt = require('bcrypt); 2、生成随机字符串gen=>generate,参数越大,生成随机字符串复杂度越高 let salt = await bcypt.genSalt(10); 3、使用随机字符串对密码进行加密 let pass = await bcrypt.hash('明文密码',salt); 4、密码比对 let isEqual = await bcrypt.compare('明文密码',salt); bcrypt依赖的环境: 1、python 2.x 2、node-gyp npm i -g node-gyp 3、windows-build-tools npm i -g windows-build-tools 下载第三方模块 npm i bcrypt -D ## cookie和session cookie 浏览器在电脑硬盘中开辟的空间,主要供服务器端存储数据 1、cookie中的数据以域名的形式进行区分 2、cookie中的数据是有过期时间的,超过时间会被浏览器自动删除 3、cookie中的数据会随着请求被自动发送到服务器端 session实际上是一个对象,存储在服务器端的内存中,服务器收到客户端请求,生成sessionID,并把sessionID发送给客户端存储在cookie,下一次请求 服务器端会获取cookie中的sessionID与自己的进行比对,判断用户是否已经登录 # session 借助express-session模块 const session = require('express-session'); app.use(session({secret:'secret key'})); ## 新增用户功能 1、为用户列表页面的新增用户按钮添加链接 2、添加一个链接对应路由 3、为新增用户表单指定请求地址、请求方式、为表单项添加name属性 4、添加实现添加用户功能的路由 5、接收到客户端传递过来的请求参数 6、对请求参数的格式进行验证 7、验证当前要注册的邮箱地址是否已经注册过 通过User.findOne({email:req.body.email}) 8、对用户发过来的密码进行加密 9、在数据库中新建用户 10、跳转回user页面 # 对请求参数进行格式上的验证 Joi # Joi JavaScript对象的规则描述语言和验证器 const Joi = require('joi); cosnt schema = joi.object{ 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() } 新增用户路由: try { // 通过joi验证用户传来的数据 await validateUser(req.body) } catch (err) { return next(JSON.stringify({ path: '/admin/user-edit', message: err.message })); }; Joi.validate({username:'abc',birthyear:1994},schema); 注意这里版本不要下最新版本,否则会报错Joi.validate is not a function 1.npm install joi@14.3.1 ## 用户列表页面设置 1、设置用户页面路由,查询用户信息 2、设置模板引擎,使数据库查询userdata显示在页面上 3、数据分页 实现分页要素: (1)当前页,用户通过点击上一页或者下一页或者页码产生,客户端通过get参数方式传递到服务器端 (2)总页数,根据总页数判断当前页是否为最后一页,根据判断结果作响应操作 limit()限制数据条数 skip()跳过多少条数据 数据开始查询位置=(当前页-1)*数目 ## 新增修改用户功能 1、将用户修改新增页面区分出来,判断是否存在id来渲染页面内容 2、建立用户信息修改功能对应的路由 3、将要修改的用户ID传递到服务器端 4、接收客户端表单传递过来的请求参数 5、根据id查询用户信息,并将客户端传递过来的密码和数据库中的密码进行比对 6、如果比对失败,对客户端做出响应 7、如果密码比对成功,将用户信息更新到数据库中 ## 新增删除用户功能 ## 新增文章功能 1、为用户列表页面的新增用户按钮添加链接 2、添加一个链接对应路由 3、为新增用户表单指定请求地址、请求方式、为表单项添加name属性 4、添加实现添加用户功能的路由 5、接收到客户端传递过来的请求参数 6、对请求参数的格式进行验证 7、验证当前要注册的邮箱地址是否已经注册过 通过User.findOne({email:req.body.email}) 8、对用户发过来的密码进行加密 9、在数据库中新建用户 10、跳转回article页面 # formidable 解析表单,支持get\post\文件上传 1、引入模块 2、创建表单解析对象 const form = new formidable.IncomingForm() 3、设置文件上传路径 form.uploadDir = '/my/dir/' 4、是否保留表单上传文件扩展名 form.keepExtensions = false; 5、对表单进行解析 form.parse(req,(err,fields,files)=>{ //fields存储普通请求参数 //files存储上传的文件信息 }) # 文件读取FileReader var reader = new FileReader(); //读取文件,而且是二进制文件,异步方法 reader.readAsDataURL('文件'); //读取完成后,onload将会调用 reader.onload = function(){ console.log(reader.result); } # 日期格式化dataformat 1、全局引入dataformat 2、模板引擎配置 template.defaults.imports.dataformat = dataformat; 3、模板中调用dataformat方法 {{dataformat($value.publishDate,'yyyy-MM-ddhh:mm:ss')}} # 数据分页 mongoose-sex-page 1、const pagination = require('mongoose-sex-page'); 2、pagination(集合构造行数).page(1).size(20).display(8).exec(); { "page":1,//当前页 "size":2,//每页显示数据条数 "total":8,//总共的数据条数 "records":[ //查询出来的具体数据 { } ], "pages":4,//总共的页数 "display":[1,2,3,4]//客户端显示的页码 } 当集合联合查询和渲染页面模板同时进行时会导致两者冲突,从而导致无法渲染页面。所以报错 解决办法: let articles = await pagination(Article).find().page(1).size(1).display(3).populate('author').exec(); let str = JSON.stringify(articles); let json = JSON.parse(str); // res.send(articles) // 渲染文章列表页面模板 res.render('admin/article.art', { articles: json }); ## mongoDB数据库添加账号 1、以系统管理员的方式运行powershell 2、连接数据库mongo 3、查看数据库show dbs 4、切换到admin数据库 use admin 5、创建超级管理员账户 db.createUser({user:'root',pwd:'liuqi254',roles:['root']}) Successfully added user: { "user" : "root", "roles" : [ "root" ] } 6、切换到blog数据库 use blog 7、创建普通账户 db.createUser() db.createUser({user:'liuqi',pwd:'123',roles:['readWrite']}) 8、卸载mongodb服务 1、停止服务 net stop mongodb 2、mongod --remove 9、创建mongodb服务 mongod --logpath="" --dbpath="" --install -auth -auth表示不能在没有账号密码的情况下操作 10、启动mongodb服务 net start mongodb 11、在项目中使用账号连接数据库 mongoose.connect('mongodb://user:pass@loaclhost:port/database'); ## 开发环境与生产环境 环境,就是项目运行的地方,不同环境中,项目的配置是不一样的。 # 如何区分开发环境与生产环境 1、先设置系统环境变量NODE_ENV 2、获取系统环境变量 返回值是对象 if (process.env.NODE_ENV == 'development') { console.log('当前是开发环境'); app.use(morgan('dev')); } else { console.log('当前是生产环境'); } # 开发环境,将客户端发送到服务器器端的请求信息打印到控制台中 const morgan = require('morgan'); app.use(morgan('dev')); ## config模块 1、允许将不同环境下的应用配置信息抽离到单独的文件中,节省了项目配置的成本 2、根据当前运行环境,自动读取对应的配置信息; 3、步骤: (1)npm install config (2)在项目根目录下新建config文件夹 (3)在config中新建default.json,development.json,production.json文件 (4)在项目中require导入 (5)使用模块内部提供的get方法获取配置信息 # 将敏感配置信息存储在环境变量中 1、在config文件夹中建立custom-environment-variables.json 2、配置项属性的值填写系统环境变量的名字 3、项目运行时config模块查找系统环境变量,并读取其值作为当前配置项属于的值,注意运行需要管理员运行powershell ## 创建首页和文章页面路由,文章页面抽离模板和公共样式,模板填坑,查询数据库,同管理-用户页面 ## 文章评论 1、创建评论集合 2、判断用户是否登录,如果用户登录,再允许用户提交评论表单 3、在服务器端创建文章评论功能对应的路由 4、在路由请求处理函数中接收客户端传递过来的评论信息 5、评论信息存储在评论集合中 6、页面重定向回文章详情页面 7、在文章详情页面路由中获取文章评论信息并展示在页面中