1 注册、登录和退出
1.1 用户注册、登录
设置模板引擎、
mongoDB
数据库驱动、静态文件途径和post
要求剖析中间件一致
api.js
路由的数据返回花样// 一致返回数据花样 var responseData; // 每次要求进来都举行初始化 router.use(function (res, req, next) { responseData = { code: 0, // 状况码,默以为0 message: '' // 状况码的提醒信息,默以为0 }; next(); // 挪用next()交由下一个中间件继承处置惩罚 });
设想用户的数据模子设想与建立
var mongoose = require('mongoose'); var Schema = mongoose.Schema; // userSchema代表名为用户的collection鸠合 var usersSchema = new Schema({ // 每一个属性代表collection中的每一个document // 用户名: 字符串 username: String, // 暗码: 字符串 password: String, // 是不是为治理员,默以为false isAdmin: { type: Boolean, default: false } }); // 对外导出定义的用户的collection组织 module.exports = usersSchema;
var mongoose = require('mongoose'); // 加载建立的usersSchema模子 var usersSchema = require('../schemas/users'); // 应用usersSchema建立Model,运用mongoose.model()要领 // 第一个参数是模子的名字;第二个参数是建立模子的数据组织 // User是一个组织函数,能够应用Model直接操纵collection,也能够实例化对象来操纵每一个document var User = mongoose.model('User', usersSchema); // 对外暴露模子,提供给营业逻辑操纵 module.exports = User;
完成注册逻辑
前端将数据提交到指定路由(Ajax或整页革新)
服务器猎取提交的数据,举行基础考证与数据库查重考证
假如数据库顶用户名已存在,返回毛病信息;假如不存在,则保留当前注册信息
完成登录逻辑
前端利将数据提交到指定路由(Ajax或整页革新)
服务器经由过程
body-parser
中间件猎取post
要求中的数据req.body
;经由过程req.query
猎取get
要求中的数据,举行基础考证举行数据库查询考证:运用
username
和password
两个字段举行查询,假如存在,则返回登录胜利;不然登录失利同时,为登录胜利的用户发送一个
cookie
,保留必要的信息,但不能是暗码等敏感信息,用于保留用户的登录状况,cookie
只要在浏览器没有上传cookie
是才发送,而且只发送一次,注重设置逾期时候// 设置Cookie,每一个要求进入路由处置惩罚前,先处置惩罚req对象中的cookie信息 // 用户不管什么时候接见站点,都通经由过程这个中间件,而且经由过程next()要领将返回值通报下去 // 在登录胜利后,经由过程cookies.set()要领将cookie一同返回给浏览器 // 第一次登录时,客户端没有cookie,须要发送一个cookie归去; app.use(function (req, res, next) { req.cookies = new Cookies(req, res); // 在接见admin中批评、留言等功用时都须要用到登录信息,定义一个全局的req对象的属性,来保留用户登录的cookie信息 req.userInfo = {}; if(req.cookies.get('userInfo')) { try { req.userInfo = JSON.parse(req.cookies.get('userInfo')); // 将cookie剖析为一个对象 // 须要及时猎取当前的用户是不是为治理员,查询数据库,应用当前用户的_id查询 User.findById({_id: req.userInfo._id}).then(function (userInfo) { req.userInfo.isAdmin = Boolean(userInfo.isAdmin); // 新增req对象的一个全局属性,推断是不是非治理员 next(); }) } catch (e) { next(); } } else { next(); } });
// 发送一个cookie,浏览器会缓存cookie,今后每次要求,浏览器都邑带上这个cookie // cookie应当能唯一标识一个用户,所以运用用户信息作为cookie,cookie是一个字符串 req.cookies.set('userInfo', JSON.stringify({ _id: userInfo._id, username: userInfo.username }));
革新页面时,浏览器将
cookie
中的数据发送到服务器,服务器应用cookie
中的信息完成登录页面的展示应用
cookie
推断是不是 为治理员(是不是是治理员的信息平常不放在cookie
中),登录背景治理界面
1.2 退出
应用cookies
模块,将cookie
字段设置为null
,然后在客户端革新页面,即未登录状况下展示的页面
router.get('/', function (req, res, next) {
res.render("admin/index.html", {
userInfo: req.userInfo
});
});
2 背景治理
2.1 推断是不是为治理员
应用
cookie
中增添的后续信息,推断是不是为治理员,只要治理员才继承后续操纵// 应用中间件推断是不是为治理员账户 router.use(function (req, res, next) { if(!req.userInfo.isAdmin) { res.send("Sorry, it's only for Administor!"); return; } // 假如是治理员,继承下面的操纵 next(); });
2.2 背景信息展示界面
应用
get
要求,猎取背景页面,传入cookie
中的信息req.userInfo
router.get('/', function (req, res, next) { res.render("admin/index.html", { userInfo: req.userInfo }); });
2.3 用户信息的展示
一样应用
get
要求,从数据库中查询一切的用户信息,并将数据返回前端。分页展示数据的功用经由过程:
limit()
和skip()
束缚完成倒序经由过程
sort({_id: -1})
束缚完成同时查询关联字段的数据经由过程
.populate(['category', 'article'])
完成router.get('/user', function (req, res, next) { /* 从数据库中读取数据,每页展示的数据数目雷同,内容不雷同 * 1、limit()束缚限定掏出数据的条数 * 2、skip()束缚限定最先取数据的位置,skip(2)示意疏忽前两天数据,从第三条最先取 * 3、每页显现4条 * 第1页: skip(0) --> 当前页 - 1 * limit * 第2页: skip(4) * 第3页: skip(8) */ var page = req.query.page || 1; // 假如用户不传,默以为第1页 var limit = 10; var pages = 0; // 保留总的页数 User.count().then(function (count) { pages = Math.ceil(count / limit); // 当前页数不能大于总页数pages page = Math.min(page, pages); // 当前页数不能小于1 page = Math.max(1, page); var skip = (page - 1) * limit; // 盘算page以后,肯定须要skip的个数 User.find().limit(limit).skip(skip).then(function (users) { res.render('admin/user_index', { userInfo: req.userInfo, users: users, count: count, // 总的数据条数 pages: pages, // 总页数 limit: limit, // 每页显现几条数据 page: page // 当前页数 }); }) }) });
2.4 分类信息的增添
构建分类信息的数据组织和模子
var mongoose = require('mongoose'); var Schema = mongoose.Schema; // categoriesSchema代表名为用户的collection鸠合 var categoriesSchema = new Schema({ // 每一个属性代表collection中的每一个document // 分类称号 name: String }); // 对外导出定义的用户的collection组织 module.exports = categoriesSchema;
var mongoose = require('mongoose'); var categoriesSchema = require('../schemas/categories'); var Category = mongoose.model('Category', categoriesSchema); module.exports = Category;
完成
get
路由猎取展示分类的页面(包含修正和删除分类的进口):经由过程数据库查询猎取一切的分类信息,一样应用limit()
、skip()
完成分页Category.find().sort({_id: -1}).limit(limit).skip(skip).then(function (categories){...}
完成
get
路由猎取分类增添的页面,应用post
要求提交数据后端经由过程
post
路由猎取增添分类的数据,举行基础考证与数据库查重:假如经由过程,则保留数据;不然返回毛病信息。应用findOne()
要领查询一条纪录;new Model().save()
要领保留数据// 分类增添的数据提交后保留 router.post('/category/add', function (req, res, next) { // 假如用户没有输入数据,或提交的数据不符合需求的花样 // 没有运用Ajax,所以不符应时直接跳转到别的一个毛病页面 var category = req.body.category || ''; if(!req.body.category) { // 假如称号为空,跳转到毛病页面 res.render('admin/error', { userInfo: req.userInfo, message: "分类称号不能为空" }); return; } // 数据库中是不是已存在雷同的分类称号 Category.findOne({name: category}).then(function (rs) { // 数据库中已存在该分类 if(rs) { res.render('admin/error', { userInfo: req.userInfo, message: "分类已存在" }); return Promise.reject(); // 退出异步实行 } else { // 数据库中不存在该分类,建立Category的实例对象保留到数据库 return new Category({ name: category }).save(); } }).then(function (newCategory) { res.render('admin/success', { // 衬着分类胜利的页面 userInfo: req.userInfo, message: "分类保留胜利", url: '/admin/category' }); }); });
2.5 分类信息的修正与删除
经由过程分类信息展示页的进口,经由过程get
要求完成分类修正页面复原;再应用post
要求将修正的数据举行更新
前端经由过程
url
传入修正和删除分类的_id
经由过程数据库查询到该条纪录,返回原有数据,衬着到编辑分类页面,展示本来的分类称号
分类修正后,应用
post
要求上传数据:_id
和修正内容背景拿到数据后,先举行基础考证;数据库考证(查询分类名是不是已存在)
Category.findOne({ id: {$ne: id}, // 差别的纪录中是不是存在雷同的分类称号 name: nameCategory })
假如分类名不存在,再应用
update()
更新本条数据// 分类信息修正保留 router.post('/category/edit', function (req, res, next) { // 猎取要修正的分类信息 var id = req.query.id; // 猎取post要求提交的分类称号数据 var nameCategory = req.body.category; // 检察提交的分类称号数据是不是存在 Category.findOne({ _id: id }).then(function (category) { if(!category) { res.render('admin/error', { userInfo: req.userInfo, message: "分类信息不存在" }); return Promise.reject(); } else { // 推断用户是不是做了修正 if(nameCategory === category.name) { // 假如没有修正,直接提醒修正胜利,跳转到首页 res.render('admin/success', { userInfo: req.userInfo, message: "修正胜利", url: '/admin/category' }); return Promise.reject(); } else { // 推断增添的分类称号是不是已存在 Category.findOne({ id: {$ne: id}, // 差别的纪录中是不是存在雷同的分类称号 name: nameCategory }).then(function (sameCategory) { if(sameCategory) { res.render('admin/error', { userInfo: req.userInfo, message: "分类称号已存在" }); return Promise.reject(); } else { // 假如不反复,则保留改的数据 Category.update({_id: id}, {name: nameCategory}).then(function () { res.render('admin/success', { userInfo: req.userInfo, message: "修正胜利", url: '/admin/category' }); }) } }) } } }) });
数据库的删除只需运用
remove({_id: id})
即可router.get('/category/delete', function (req, res, next) { // 猎取要删除分类的id var id = req.query.id; Category.remove({_id: id}).then(function () { res.render('admin/success', { userInfo: req.userInfo, message: "删除胜利", url: '/admin/category' }); }) });
2.6 文章治理
文章治理的完成逻辑与分类治理基础一致:
完成文章治理的列表展示页,有增添文章的进口
// 文章首页 router.get('/article', function (req, res, next) { // 从数据库猎取文章的内容 var page = req.query.page || 1; var limit = 10; var pages = 0; // 分类治理时,基本知识应当讲新增添的分类放在最前面,所以展示时应当降序从数据库中读取数据 Article.count().then(function (count) { pages = Math.ceil(count / limit); page = Math.min(page, pages); page = Math.max(1, page); var skip = (page - 1) * limit; // sort()束缚有两个值:1示意升序;-1示意降序 Article.find().sort({_id: -1}).limit(limit).skip(skip).populate(['category', 'user']).then(function (articles) { console.log(articles); res.render('admin/article_index', { userInfo: req.userInfo, articles: articles, pages: pages, count: count, limit: limit, page: page }); }); }); });
完成文章增添页的表单,经由过程
post
提交填写的数据router.get('/article/add', function (req, res, next) { // 从服务器中读取一切分类信息 Category.find().sort({_id:-1}).then(function (categories) { res.render('admin/article_add', { userInfo: req.userInfo, categories: categories }); }); });
后端剖析猎取文章的数据,举行基础考证,经由过程后举行数据保留
// 文章内容保留路由 router.post('/article/add', function (req, res, next) { var categoryId = req.body.category; var description = req.body.description; var title = req.body.title; var article = req.body.article; // console.log(categoryId, description, title, article); // 基础考证,分类、题目、简介、内容不能为空 if(!categoryId) { res.render('admin/error', { userInfo: req.userInfo, message: "文章分类不能为空" }); return; } if(!title) { res.render('admin/error', { userInfo: req.userInfo, message: "文章题目不能为空" }); return; } if(!description) { res.render('admin/error', { userInfo: req.userInfo, message: "文章简介不能为空" }); return; } if(!article) { res.render('admin/error', { userInfo: req.userInfo, message: "文章内容不能为空" }); return; } // 保留数据到数据库 return new Article({ // 应用Model建立一个实例对象,应用save()要领保留数据 category: categoryId, user: req.userInfo._id.toString(), title: title, description: description, article: article }).save().then(function () { res.render('admin/success', { userInfo: req.userInfo, message: "文章增添胜利", url: '/admin/article' }); }) });
文章的修正有两个步骤:首先是猎取文章的编辑页,将本来的内容衬着到页面上,编辑修正后,将数据提交到背景;背景完成基础考证后,更新数据库中的内容
// 文章内容修正的数据提交 router.post('/article/edit', function (req, res, next) { // 猎取文章的id var id = req.query.id; var category = req.body.category; var title = req.body.title; var description = req.body.description; var article = req.body.article; if(!category) { res.render('admin/error', { userInfo: req.userInfo, message: "文章分类不能为空" }); return; } if(!title) { res.render('admin/error', { userInfo: req.userInfo, message: "文章题目不能为空" }); return; } if(!description) { res.render('admin/error', { userInfo: req.userInfo, message: "文章简介不能为空" }); return; } if(!article) { res.render('admin/error', { userInfo: req.userInfo, message: "文章内容不能为空" }); return; } Article.update({_id: id}, { category: category, description: description, title: title, article: article }).then(function () { res.render('admin/success', { userInfo: req.userInfo, message: '内容修正胜利', url: '/admin/article' }) }); });
文章的删除与分类删除一致:运用
remove()
要领router.get('/article/delete', function (req, res, next) { var id = req.query.id || ''; if(!id) { res.render('admin/error', { userInfo: req.userInfo, message: '指定 文章不存在' }) return; } Article.remove({_id: id}).then(function () { res.render('admin/error', { userInfo: req.userInfo, message: '删除胜利', url: '/admin/article' }); }) });
2.7 批评的治理
批评能够零丁存放在一个
coolection
中,便于种种操纵治理,通用性强,能够编辑和删除;增添复杂度将批评存储为文章的一个字段,与一片文章绑定在一同,操纵轻便,然则编辑与删除功用很难完成
3 前台展示
经由过程后端将数据返回以后,服务器端平常将数据组织为JSON
花样,便于操纵,能够应用后端模板或许前端操纵DOM
的体式格局将数据增添到页面。
前端衬着:能够减轻服务器端的开支,然则首屏的衬着会加长时候
后端衬着:增添服务器的开支,然则削减客户端展示的时候