我的项目文件夹构造

建设我的项目所需文件夹

  • 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.art

views/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依赖的其余环境

  1. python 2.x
    装置好后增加环境变量 也就是装置门路
  2. node-gyp
    npm install -g node-gyp
  3. windows-build-tools
    npm install --global --production windows-build-tools
  4. 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);

数据分页

当数据库中的数据十分多时,数据须要分批次显示,这时就须要用到数据分页性能。
分页性能外围因素:

  1. 当前页,用户通过点击上一页或者下一页或者页码产生,客户端通过get参数形式传递到服务器端
  2. 总页数,依据总页数判断当前页是否为最初一页,依据判断后果做响应操作
总页数: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属性 也就是创立网站服务器的appreq.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=10multipart/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