我的项目文件夹构造
建设我的项目所需文件夹
- public 动态资源
款式文件、脚本文件、图片文件 - model 数据库操作
创立汇合的文件和连贯数据库的文件 - route 路由
路由文件 - views 模板
模板文件
route 文件夹:(模块化路由)
route/home 文件夹
route/admin 文件夹route 文件夹下的文件
route/home.js
形容:博客展现页面路由// 援用 express 框架 const express = require('express'); // 创立博客展现页面路由(二级路由) const home = express.Router();
将回调函数放入 route 文件夹下的 home 文件夹中
// 博客前台首页的展现页面 + 路由优化 home.get('/', require('./home/index'));
// 将路由对象作为模块成员进行导出 module.exports = home;
route/admin.js
形容:博客治理页面路由// 援用 express 框架 const express = require('express'); // 创立博客展现页面路由 const admin = express.Router();
// 将路由对象作为模块成员进行导出 module.exports = admin;
回调函数在 route 文件夹下的 admin 文件夹中
// 渲染登录页面 admin.get('/login', require('./admin/loginPage'));
注 :res.render() 办法中第一个参数要传递模板门路,默认状况下要传递绝对路径。但思考到渲染模板有很多所以须要在 app.js 中进行配置 —(标注 1)
// 实现登入性能 admin.post('/login', require('./admin/login'));
// 创立用户列表路由 admin.get('/user', require('./admin/userPage'));
// 实现退出性能 admin.get('/logout', require('./admin/logout'));
// 创立用户编辑页面路由 admin.get('/user-edit', require('./admin/user-edit'));
// 创立实现用户增加性能路由 admin.post('/user-edit', require('./admin/user-edit-fn'));
// 创立用户信息批改性能路由 admin.post('/user-modify', require('./admin/user-modify'));
// 删除用户性能路由 admin.get('/delete', require('./admin/user-delete'));
// 文章列表页面路由 admin.get('/article', require('./admin/article'));
// 文章编辑页面路由 admin.get('/article-edit', require('./admin/article-edit'));
// 实现文章增加性能的路由 admin.post('/article-add', require('./admin/article-add'));
views 文件夹:(模板文件)
views/home 文件夹
views/home/common文件夹
home 文件夹下的文件:
views/home/article.art
views/home/default.artviews/admin 文件夹
views/admin/common 文件夹(模板的公共局部)
子模版的相对路径是绝对于以后文件的(由模板引擎解析)views/admin/common/aside.art(侧边栏)
views/admin/common/header.art(头部)
views/admin/common/layout.art(HTML 骨架)views/admin 文件夹下的文件:
模板外部外链资源文件问题(外链资源是由浏览器解析的):
外链资源即模板外部外链的 css\js\img 文件。须要 留神 的是模板文件中的外链资源的相对路径是绝对与浏览器地址栏中的申请门路的, 如:http://localhost/admin/login
地址,浏览器认为 login 为门路下的文件,/admin 为申请门路,所以模板中的相对路径是绝对与 /admin 的。
为保障外链资源可能失常被拜访,应该应用绝对路径。因为在 app.js 中凋谢了动态资源 (public 文件夹), 所以应用绝对路径拜访的是 public 文件夹下的资源,
如:<link rel="stylesheet" href="/admin/css/base.css">
在
"css/base.css"
相对路径前加上了/admin/
views/admin/login.art
为登录表单设置申请地址及申请形式<form action="/admin/login" method="post" id="loginForm"></form>
为表单项设置 name 属性如:
<input name="email" type="email" class="form-control" placeholder="请输出邮件地址">
注:要阻止表单默认提交行为
<script src="/admin/js/common.js"></script>
<script>
// 为表单增加提交事件
$('#loginForm').on('submit',function() {var result = serializeToJson($(this));
//trim 去除字符串两端空格
if (result.email.trim().length == 0) {alert('请输出邮件地址');
// 阻止程序向下执行
return false;
}
if (result.password.trim().length == 0) {alert('请输出明码');
// 阻止程序向下执行
//return false 阻止表单默认提交行为
return false;
}
})
</script>
// 在 public/admin/js/common.js 中
function serializeToJson(form) {var result = {};
//.serializeArray()是 JQury 提供的办法 获取到表单中用户输出的内容
//[{name: "email", value: "uwhdb@gv"},{name: "password", value: "1223"}]返回内容是一个数组
var f = form.serializeArray();
f.forEach(function(item) {result[item.name] = item.value;
});
return result;
}
views/admin/user.art
views/admin/user-edit.art
views/admin/error.art
views/admin/article.art
views/admin/article-edit.art
model 文件夹:
model/connect.js
连贯数据库// 引入 mongoose 第三方模块 const mongoose = require('mongoose');
// 连贯数据库 mongoose.connect('mongodb://localhost/blog', { useNewUrlParser: true, useUnifiedTopology: true}).then(() => console.log('数据库连贯胜利')).catch(() => console.log('数据库连贯失败'));
model/user.js
// 引入 mongoose 第三方模块 const mongoose = require('mongoose');
// 创立用户汇合规定 const userSchema = new mongoose.Schema({});
// 创立用户汇合 const User = mongoose.model('User', userSchema);
//ES6 中如果对象的键和值名称一样 能够省略值 module.exports = {User,validateUser};
model/article.js
// 引入 mongoose 第三方模块 const mongoose = require('mongoose');
// 创立文章汇合规定 const articleSchema = new mongoose.Schema
// 依据规定创立汇合 const Article = mongoose.model('Article', articleSchema);
// 将汇合规定作为模块成员进行导出 module.exports = {Article}
model/comment.js
根目录下的文件
package.json 文件
能够通过初始化我的项目形容文件失去// 命令行中输出 npm init -y
app.js 文件
形容:我的项目入口文件
app.js 文件须要进行的操作:// 援用 express 框架 const express = require('express'); // 创立网站服务器 const app = express();
// 监听端口 app.listen(80); console.log('网站服务器启动胜利');
配置路由
// 导入路由模块 const home = require('./route/home'); const admin = require('./route/admin');
// 为路由匹配申请门路 app.use('/home', home); app.use('/admin', admin);
配置动态资源
// 引入 path 零碎模块 const path = require('path');
// 凋谢动态资源文件 app.use(express.static(path.join(__dirname, 'public')));
模板配置 – 对本文中标注 1 进行解释
// 当渲染后缀为 art 的模板时 所应用的模板引擎是什么 以便应用 res.render()办法 app.engine('art', require('express-art-template')); // 通知 express 框架模板所在位置 app.set('views', path.join(__dirname, 'views')); // 通知 express 框架模板的默认后缀 app.set('view engine', 'art');
// 数据库连贯 require('./model/connect');
// 引入 body-parser 模块 用来解决 post 申请参数 const bodyParser = require('body-parser');
// 解决 post 申请参数 //extended: false 示意应用零碎模块 querystring 解析 而不是其余第三方模块 app.use(bodyParser.urlencoded({ extended: false}));
// 导入 express-session 模块 const session = require('express-session');
// 配置 session secret 的值能够自定义 //saveUninitialized false 没有登录的状况下不保留 cookie // cookie: {maxAge: 24 * 60 * 60 * 1000} 设置 cookie 的过期工夫 app.use(session({secret: 'secret key',saveUninitialized: false,cookie: {maxAge: 24 * 60 * 60 * 1000}}));
// 拦挡申请 判断用户登录状态 //use 的第一个参数 /admin 是指以 /admin 结尾的路由 app.use('/admin', require('./middleware/loginGuard'));
我的项目蕴含的知识点
明码加密 bcrypt
哈希加密是单程加密形式:1234 => abcd
在加密的明码中退出随机字符串能够减少明码被破解的难度。// 导入 bcrypt 模块 const bcrypt = require('bcrypt'); // 生成随机字符串 gen => generate 生成 salt 盐 let salt = await bcrypt.genSalt(10); // 应用随机字符串对明码进行加密 let pass = await bcrypt.hash('明文明码', salt);
// 明码比对 let isEqual = await bcrypt.compare('明文明码', '加密明码');
bcrypt 依赖的其余环境
- python 2.x
装置好后增加环境变量 也就是装置门路- node-gyp
npm install -g node-gyp
- windows-build-tools
npm install --global --production windows-build-tools
- bcrypt
npm install bcrypt
cookie 与 session
网站利用是基于 http 协定,即申请与相应模型的利用。特点是:在实现一次客户端和服务器端的申请与响应后,客户端与服务端便没有分割了(http 协定的无状态性)
cookie:浏览器在电脑硬盘中开拓的一块空间,次要供服务器端存储数据。
- cookie 中的数据是以域名的模式进行辨别的。
- cookie 中的数据是有过期工夫的,超过工夫数据会被浏览器主动删除。如果没有设置过期工夫,浏览器在敞开的时候就会删除。
- cookie 中的数据会随着申请被主动发送到服务器端。
session:实际上就是一个对象,存储在服务器端的内存中,在 session 对象中也能够存储多条数据,每一条数据都有一个 sessionid 做为惟一标识。
在 node.js 中须要借助 express-session 实现 session 性能。
const session = require('express-session'); app.use(session({ secret: 'secret key'})); // 为申请对象上面增加 session 属性 //secret key 密钥值能够自定义
Joi
JavaScript 对象的规定描述语言和验证器。
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() }; Joi.validate({username: 'abc', birthyear: 1994}, schema);
数据分页
当数据库中的数据十分多时,数据须要分批次显示,这时就须要用到数据分页性能。
分页性能外围因素:
- 当前页,用户通过点击上一页或者下一页或者页码产生,客户端通过 get 参数形式传递到服务器端
- 总页数,依据总页数判断当前页是否为最初一页,依据判断后果做响应操作
总页数:Math.ceil(总数据条数 / 每页显示数据条数)limit(2) // limit 限度查问数量 传入每页显示的数据数量 skip(1) // skip 跳过多少条数据 传入显示数据的开始地位 数据开始查问地位 =(当前页 -1)* 每页显示的数据条数
let page = req.query.page || 1; // 每一页显示的数据条数 let pagesize = 10; // 查问用户数据的总数 用户汇合 User 下有 countDocuments()办法 let count = await User.countDocuments({}); // 总页数 向上取整 let total = Math.ceil(count / pagesize); // 页码对应的开始地位 let start = (page - 1) * pagesize; // 将用户信息从数据库中查问进去
// 渲染用户列表模板
res.render('admin/user', {
users: users,
page: page,
total: total
});
formidable
作用:解析表单,反对 get 申请参数,post 申请参数、文件上传。
// 引入 formidable 模块 const formidable = require('formidable'); // 创立表单解析对象 const form = new formidable.IncomingForm(); // 设置文件上传门路 form.uploadDir = "/my/dir"; // 是否保留表单上传文件的扩展名 form.keepExtensions = ture; // 对表单进行解析 form.parse(req, (err, fields, files) => { // fields 存储一般申请参数 // files 存储上传的文件信息 });
文件读取 FileReader
var file = document.querySelector('#file');
var preview = document.querySelector('#preview');
// 当用户抉择完文件当前
file.onchange = function() {
// 创立文件读取对象
var reader = new FileReader();
// 用户抉择的文件列表 this.files
// this.files[0] 在一组用户中抉择第一个文件
// 读取文件 .readAsDataURL()是异步办法
reader.readAsDataURL(this.files[0]);
// 监听 onload
reader.onload = function() {
// 将文件读取的结果显示在页面中
preview.src = reader.result;
}
}
系统知识点
// 依据 app.js 文件中 const app = express(); // 申请对象上面有一个 app 属性 也就是创立网站服务器的 app req.app
// 把公共数据裸露到模板中 app.locals
//express 框架下的页面重定向办法 res.redirect('');
//.redirect 除了重定外 还做了 res.end 这步操作 所以要 return // 不然命令行会报错 return res.redirect(`/admin/user-edit?message= 邮箱地址曾经被占用 `);
// 模板语法 @示意原文输入 <td>{{@$value._id}}</td>
//express 框架中的错误处理中间件
app.use((err, req, res, next) => {
// 将字符串对象转换为对象类型
//JSON.parse()
const result = JSON.parse(err);
let params = [];
for (let attr in result) {if (attr != 'path') {params.push(attr + '=' + result[attr]);
}
}
// res.redirect(`${result.path}?message=${result.message}`)
res.redirect(`${result.path}?${params.join('&')}`);
})
触发错误处理中间件
try {
// 代码优化 申请验证解决代码放在了 user.js 中
await validateUser(req.body);
} catch (err) {
// 验证没有通过
//next()里只能传递一个字符串参数
//JSON.stringify()将对象数据类型转换为字符串数据类型
// 代码优化 错误处理优化
return next(JSON.stringify({ path: '/admin/user-edit', message: err.message}))
}
// 减号有隐式类型转换 而加号为字符串拼接 <a href="/admin/user?page=<%=page-1%>"> <a href="/admin/user?page=<%=page-0+1%>">
如果表单要进行文件上传,表单的数据必须以二进制的数据上传
<!-- form 下的属性 enctype 指定表单的编码类型 默认值为 application/x-www-form-urlencoded 如:name=zhangsan&age=10 multipart/form-data 将表单数据编码成二进制类型 -->
因为 body-paser 只能解决一般表单传递过去的参数,而不能解决二进制数据
应用要应用 formidable 第三方模块字符串下有 split 字符串宰割办法
// 分隔符为 public, 返回值是一个数组, 取数组的第二个值 files.cover.path.split('public')[1]
我的项目所需的第三方模块
// 命令行中输出
npm install express mongoose art-template express-art-template
在 express 中接管 post 申请参数须要用到第三方模块 body-paeser
// 命令行中输出
npm install body-parser
npm install -g node-gyp
npm install --global --production windows-build-tools
npm install bcrypt
npm install express-session
npm install joi
npm install formidable