1.环境部署搭建
-
下载node.js
- 安装包下载地址 :http://nodejs.cn/download/
-
下载MongDB
- 64位安装包下载地址:https://www.mongodb.org/dl/win32
- 32为安装包下载地址:https://www.mongodb.org/dl/win32/i386
- 安装目录添加到环境变量
- 配置服务
mongod --dbpath "C:\mongodb\db" --logpath "C:\mongodb\log.txt" --install --serviceName "MongoDB"
- 启动服务
net start MongoDB
- 进入MongDB
mongo
-
项目文件
- 安装自动重启nodemon
npm install -g nodemon
- 初始化项目
npm init -y
- 安装项目第三方依赖
npm install express mongoose body-parser art-template express-art-template bootstrap blueimp-md5 jquery express-session joi formidable dateformat
- 创建入口 app.js
- 创建文件夹
public 静态资源
model 数据库操作
route 路由
views 模板视图
middleware 中间件
- 安装自动重启nodemon
2.配置app.js,启动服务
const express = require('express');
const app = express();
app.listen(3000);
console.log("服务启动")
3.设计配置项目路由
请求路径 | 请求方法 | 请求参数 | 是否需要登录 | 备注 |
---|---|---|---|---|
/ | get | 无 | 否 | 渲染首页 |
/admin /login | get | 无 | 否 | 渲染登录页 |
/admin/login | post | email、password | 否 | 处理登录业务 |
/admin/register | get | 无 | 否 | 渲染注册页 |
/admin /register | post | email、nickname、password | 否 | 处理注册业务 |
/admin/user | get | page | 是 | 渲染所有用户页面,需要权限 |
/admin/admin | get | 无 | 是 | 渲染用户页面 |
/admin/admin | post | password、Newpassword | 是 | 处理用户修改密码业务 |
/admin/delete | get | id | 是 | 处理删除用户业务 |
/admin/profile | get | id | 是 | 渲染用户信息页面 |
/admin/profile | post | id、nickname、bio、gender、brithday、avatarr | 是 | 修改用户信息业务 |
/topic/show | get | 无 | 否 | 渲染评论页面 |
/topic/new | get | 无 | 是 | 渲染添加评论页面 |
/topic/new | post | 有 | 是 | 处理添加评论业务 |
/topic/edit | get | id | 是 | 渲染修改评论页面 |
/topic/edit | post | 有 | 是 | 处理修改评论业务 |
- 首页渲染
app.get('/',(req,res) => {
res.render('index');
})
- admin路由
const express = require('express');
//创建admin路由
const admin = express.Router();
admin.get('/', (req, res) =>{
res.render('admin')
})
//其他页面路由根据表格设计
module.exports = admin;
- 创建topic路由,设计思路同上
- 拦截分配路由
const admin = require('./route/admin');
const topic = require('./route/topic');
app.use('/admin',admin);//admin目录文件启用admin路由
app.use('/topic',topic);//topic目录文件启用topic路由
4.配置模板
- 开放资源
const path = require('path');
app.use('/public/',express.static(path.join(__dirname,'./public/')));
app.use('/node_modules/',express.static(path.join(__dirname,'./node_modules/')));
- 设置模板默认路径和后缀
app.engine('html',require('express-art-template'))
app.set('views',path.join(__dirname,'./views/'));
app.set('view engine', 'html');
- 设置404页面跳转
app.use( (req, res) => {
res.render('404');
})
- 配置,继承模板
{{include '../_partials/header.html'}}
相对路径、模板引入
{{extend '../_layouts/home.html'}}
相对路径、模板继承
{{block 'title'}}默认{{/block}}
设置'title'默认内容或者继承模板后修改'title'内容
5.实现项目功能
5.1 数据库功能
- 数据库连接
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/blog')
.then(() => console.log('数据库连接成功'))
.catch(() => console.log('数据库连接失败'));
- 引入数据库连接
require('./model/connect.js');
- 创建用户集合
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
email:{
type: String,
required: true
},
nickname:{
type: String,
required: true
},
password:{
type: String,
required: true
},
created_time:{
type: Date,
default:Date.now
},
last_modified_time:{
type: Date,
default:Date.now
},
avatar:{
type: String,
default:'/public/img/avatar-default.png'
},
bio:{
type: String,
default:''
},
gender:{
type: Number,
enum:[-1,0,1],
default:-1
},
birthday:{
type: Date
},
status:{
type: Number,
enum:[0,1,2],
default:0
}
});
const User = mongoose.model('User',userSchema);
module.exports = { User };
- 创建评论集合,设计思路同上
5.2 引入body-parser
// 引入后可以使用req.body 接收参数
const bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({extended:false}));
app.use(bodyParser.json());
5.3 登录功能
- 给表单添加按钮事件,并根据服务器返回的信息进行提示
<script>
$('#login_form').on('submit', function (e) {
e.preventDefault()
if($('#email').val().length === 0){
$('#message').html('邮箱不为空');
return false
}
if($('#nickname').val().length === 0){
$('#message').html('昵称不为空');
return false
}
if($('#password').val().length === 0){
$('#message').html('密码不为空');
return false
}
var formData = $(this).serialize()
$.ajax({
url: '/admin/register',
type: 'post',
data: formData,
dataType: 'json',
success: function (data) {
console.log(data.message);
if(data.message =='OK'){
window.location.href = '/'
}
$('#message').html(data.message)
}
})
})
</script>
- 引入express-session并且配置
const session = require('express-session');
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized:true
}));
- 给路由设计登录功能
//引入用户构造函数
const { User,validateUser } = require('../../model/user');
//引入密码加密
const md5 = require('blueimp-md5');
//可用随机字符串加大破解难度,另可用Bcrypt加密,这个对环境依赖比较高
const md5random = 'yWycs';
module.exports = async (req,res,next) => {
const{email,password} = req.body;
if(email.trim().length === 0 || password.trim().length === 0){
//如果浏览器无输入不继续往下执行,并把错误信息传递登录页
return next(JSON.stringify({message: '昵称或者邮箱未输入'}));
}
//根据邮箱查询数据,对象中的变量名和值相同可以省略成一个,{email:email}
let user = await User.findOne({ email });
if(user){
if(user.password !== md5(md5(password))+md5random){
return next(JSON.stringify({message: '密码错误!'}));
}
req.session.username = user.nickname;
req.app.locals.userInfo = user;//不用重复传递数据进行渲染
return next(JSON.stringify({message: 'OK'}));
}else{
return next(JSON.stringify({message: '这个邮箱不存在'}));
}
}
- 实现登录功能,和页面渲染
admin.get('/login',require(./admin/login));
admin.post('/login',require(./admin/login-fn));
- 根据路径,判断是否需要登录
const adminGuard = (req,res,next) =>{
if (req.url !='/login' && !req.session.username){
res.redirect('/admin/login');
}else{
next();
}
}
const topicGuard = (req,res,next) =>{
if (req.url !='/' && !req.session.username){
res.redirect('/admin/login');
}else{
next();
}
}
module.exports = {adminGuard,topicGuard}
- 引用中间件,拦截请求 必须在分配路由之前
const { adminGuard , topicGuard } = require('./middleware/loginGuard');
app.use('/admin',adminGuard);
app.use('/topic',topicGuard);
5.4 注册功能
- 设计验证规则
const Joi = require('joi');
const validateUser = user => {
const schema = {
email:Joi.string().email().required().error(new Error('邮箱不符合规则')),
nickname:Joi.string().min(2).max(10).required().error(new Error('用户名不符合规则')),
password:Joi.string().regex(/^[a-zA-Z0-9]{3,30}$/).required().error(new Error('密码不符合规则'))
};
return Joi.validate(user,schema);
}
module.exports = {User,validateUser};
- 结合路由设计,注册功能
const { User,validateUser } = require('../../model/user');
//引入密码加密
const md5 = require('blueimp-md5');
//可用随机字符串加大破解难度,另可用Bcrypt加密,这个对环境依赖比较高
const md5random = 'yWycs';
module.exports = async (req,res,next) => {
//验证用户传递的信息
try {
await validateUser(req.body);
}
catch (e) {
return next(JSON.stringify({message: e.message}));
}
//查找邮箱,昵称是否已经被注册,用MongoDB的$or进行查询
let user = await User.findOne({$or:[{email:req.body.email},{nickname:req.body.nickname}]});
if (user){
return next(JSON.stringify({message: '昵称或者邮箱已存在,请重试'}));
}
req.body.password = md5(md5(req.body.password ))+md5random;
await User.create(req.body);
//注册成功跳转到登录页,把邮箱传过去吧
return next(JSON.stringify({message: 'OK'}));
}
- 错误信息中间件
app.use((err,req,res,next) =>{
const result = JSON.parse(err);
res.status(200).json({
message: result.message
});
});
- 实现注册功能和页面渲染
admin.get('/register',require('./admin/register'));
admin.post('/register',require('./admin/register-fn'));
5.5用户修改功能实现
- 因为有登录拦截,如果登录成功的话用户信息保存在
app.locals
直接渲染即可
//如果是用session保存用户信息,把session传递即可
admin.get('/profile',require('./admin/profile'));
admin.get('/profile',require('./admin/admin'));
- 添加修改用户信息功能
const { User } = require('../../model/user');
//获取文件上传需引用formidable
const formidable = require('formidable');
const path = require('path');
module.exports = (req,res,next) =>{
const form = new formidable.IncomingForm();
form.uploadDir = path.join(__dirname,'../../public/upload/avatar');
form.keepExtensions = true;
form.parse(req,async (err,fields,files) =>{
const {nickname,bio,gender,birthday,avatar} = fields;
const avatar = files.avatar.path.split('public')[1];
await User.updateOne({_id: req.app.locals.userInfo['id']},{
nickname:nickname,
bio:bio,
gender:gender,
birthday:birthday,
avatar:avatar
});
return next(JSON.stringify({message: 'OK'}));
}
}
- 页面上传图片即时预览
var file = document.querySelector('#file');
var preview = document.querySelector('#preview');
file.onchange = function(){
var reader = new FileReader();
reader.readAsDataURL(this.file[0]);
reader.onload = function(){
preview.src = reader.result;
}
}
- 添加修改密码功能
const { User } = require('../../model/user');
const md5 = require('blueimp-md5');
const md5random = 'yWycs';
module.exports = async (req,res,next) => {
const {password,Npassword} = req.body;
const Npassword2 = md5(md5(Npassword))+md5random;
//检验原密码
if ( req.app.locals.userInfo['password'] !== md5(md5(password ))+md5random){
return next(JSON.stringify({message: '密码不对,请重新输入原密码'}));
} else if( req.app.locals.userInfo['password'] === Npassword2){
return next(JSON.stringify({message: '新密码不应该和原密码相同'}));
} else {
await User.updateOne({_id: req.app.locals.userInfo['id']},{
password:Npassword2
});
req.app.locals.userInfo['password'] = Npassword2;
return next(JSON.stringify({message: 'OK'}));
}
}
- 实现修改功能
admin.get('/profile',require('./admin/profile-fn'));
admin.get('/profile',require('./admin/admin-fn'));
5.6用户注销功能实现
- 用户删除功能
//使用同步,可以用res.redirect('/')进行重定向
const { User } = require('../../model/user');
module.exports = async (req,res) => {
const id = req.app.locals.userInfo['id'];
console.log(id);
await User.findOneAndDelete({_id:id});
req.session.username = null;
req.app.locals.userInfo = null;
res.redirect('/');
}
- 实现修改功能
admin.get('/user-delete',require('./admin/user-delete'));
5.7管理所有用户功能实现
- 查询所有用户
const { User } = require('../../model/user');
module.exports = async (req,res) => {
let users = await User.find({}).limit(pagesize).skip(start);
res.render('admin/user',{
users:users,
page:page,
total:total
});
}
- 渲染
admin.get('/user',require('./admin/userPage'));
- 数据分页
let page = req.query.page || 1;
let pagesize = 10;
let count = await User.countDocuments({});
let total = Math.ceil(count/pagesize);
let start = (page - 1) * pagesize;
- 页面分页代码
<li>
<a style="display: <%= page-1 < 1 ? 'none':'inline'%>;" href="/admin/user?page=<%=page-1 %>">上一页</a>
</li>
<% for(var i =1;i<=total;i++){%>
<li class="<%=page==i?'active':''%>"><a href="/admin/user?page=<%=i %>"><%=i%></a></li>
<% }%>
<li>
<a style="display: <%= page-0+1 > total ? 'none':'inline'%>;" href="/admin/user?page=<%=page-0+1 %>">下一页</a>
</li>
5.7用户退出
- 删除userInfo和session
admin.get('/logout', (req, res) =>{
req.session.username = null;
req.app.locals.userInfo = null;
res.redirect('/');
})
以上是思路是先写后验证,如果有错,请帮忙指出