ejs-express-mysql
##前言
也是这两周才正式的接触node,虽然在前端开发中我们常常说前后端分离,但是在学习过程中,个人感觉还是要刁难刁难自己的。因为用ejs来写前端页面。
项目主要实现用户的登录,session的存储和加密(准确的说是签名),数据库的CRUD,包括图片的上传,删除和修改等基本功能。
关于登录,查询等操作本应该更加的严谨,这里只做简单演示。包括一些配置文件的编写。
喜欢的朋友方便的话可以给个star (^-^)V
顺便推广一波nodejs技术交流群,群号:209530601
ejs mysql nodejs express express-router...
##效果图
登录页
整体操作流程图
GIF Brewery转gif真的有点。。。好吧,不吐槽了,后面会分开讲解每一步,好在基本操作还能看得清~
后台管理首页
博文管理
用户管理页
操作
##开发准备
关于开发前期的准备,这里就不多说了,说实话,自己也没有准备啥,关于nodejs环境,MySQL配置啥的就多少了,关于本项目的数据字典,还有SQL文件已经在目录里了,这里主要说下后端开发的每一个步骤
##项目目录
项目目录
##整体架构
项目重点在后端开发中,web端页面并没有涉及到,后端管理流程大致如下:
路由控制分为admin,web,还是那句话,我们操作全部在admin中
跳转到admin拦截所有的请求,判断用户是否登录
未登录则重定向到登录,登陆成功后设置session。不懂session?点击这里
登录后则可进行相关的操作,数据的增删改查等功能。
##后端开发
###后台基本架构、路由设置
const express = require('express');
const expressStatic = require('express-static');
const bodyParser = require('body-parser');
const multer = require('multer');
const multerObj = multer({dest:'./static/upload'});
const cookieParser = require('cookie-parser');
const cookieSession = require('cookie-session');
const consolidate = require('consolidate');
const ejs = require('ejs');
//创建服务器
var server = express();
server.listen(8080);
//解析请求数据
server.use(bodyParser({
extended:false
}));
server.use(multerObj.any());
//设置cookie,session
server.use(cookieParser('Neal_signed'));
(function () {
var arr = [];
for(var i = 0;i<10000;i++){
arr.push('keys_'+Math.random());
}
server.use(cookieSession({
name:'session_id',
keys:arr,
maxAge:20*60*1000//一般我会设置20分钟,这里是为了感受session过期~~带来的快感~?(●´∀`●)ノ
}))
})();
//设置模板
server.set('view engine','html');
server.set('views','./views');
server.engine('html',consolidate.ejs);
//设置路由
server.use('/admin',require('./router/admin/index')());
server.use('/',require('./router/web/index')());
//静态文件的请求
server.use('/files',expressStatic('./static'));
我的基本架构如下,关于每一部分的功能,都已经标注。关于路由的控制在admin/index.js跟server.js大同小异,我想大家也都应该知道了。
###登录功能
登录功能这里主要说两点
密码的md5签名(当然,大多数人说是md5加密)
session的应用
在lib中存放着自己写的一些方法,作为一个库,admin初始化有三个用户,包括u/p:root,neal,Nealyang
关于密码的签名方法主要如下:
var crypto = require('crypto');
module.exports = {
MD5_SUFFIX : 'JDSAIOEUQOIoieuoiqv#$%^&dhfja)(* %^&FGHJfyuieyfhfhak(^.^)YYa!!\(^o^)/Y(^o^)Y(*^__^*)ヘ|・∀・|ノ*~●',
md5:function (pwd) {
var md5 = crypto.createHash('md5');
return md5.update(pwd).digest('hex');
}
};
MD5_SUFFIX是加密字符串,用法如路由login.js:
router.post('/',function (req,res) {
var username = req.body.username;
var password = common.md5(req.body.password+common.MD5_SUFFIX);
if(username && password){
db.query('SELECT * FROM admin_table WHERE username="'+username+'"',function (err,userData) {
if(err){
console.error(err);
res.status(500).send({code:500,data:[],msg:'database error'});
}else if(userData.length == 0){
res.status(400).send({code:400,data:[],msg:'parameters error'});
}else{
if(userData[0].password != password){
res.status(400).send({code:400,data:[],msg:'username or password error'});
}else{
req.session['user_id'] = userData[0].ID;//注意这里是在req上面
res.status(200).send({code:200,data:[],msg:'success'});
}
}
})
}else{
res.status(400).send({code:400,data:[],msg:'parameters error'});
}
});
从上面的代码中也展现了什么时候设置session,并且值得提一下的是这里提供给前端页面的是接口,这样的话很多逻辑都放到了前端,后面我们都是通过页面渲染来输出的了。下面是所有请求的拦截判断:
router.use(function (req,res,next) {
if(!req.session['user_id'] && req.url != '/login'){
res.redirect('/admin/login');
}else{
next();
}
});
##ejs前端页面的重点代码讲解
公共头部的引入:
查询数据库的前端展示:
删除
修改
修改后我们通过页面给的标识来知道是否为修改的提交,毕竟这里我们没有前端逻辑的js
标题:
作者:
摘要:
链接:
##博文管理
还是那句话,如果是仅仅提供前端的接口的话,这里会方便很多,然后我们用的是ejs,所以很多的逻辑都放在了后端,在get/post到请求的时候需要做很多判断
get方法请求如下:
router.get('/', function (req, res) {
switch (req.query.action) {
case 'del':
//删除操作
db.query('DELETE FROM blog_list_table WHERE id="'+req.query.id+'"',function (err,resultData) {
if(err){
console.error(err);
res.status(500).send({code:500,msg:'database error'});
}else{
res.redirect('/admin/blog');
}
});
break;
case 'mod':
//修改操作
db.query('SELECT * FROM blog_list_table WHERE id="'+req.query.id+'"',function (err,modData) {
if(err){
console.error(err);
res.status(500).send({code:500,msg:'database error'});
}else if(modData.length == 0){
res.status(400).send({code:400,msg:'parameters error'});
}else{
db.query('SELECT * FROM blog_list_table',function (err,allData) {
if(err){
console.error(err);
res.status(500).send({code:500,msg:'database error'});
}else{
res.render('admin/blog.ejs',{formData:allData,modData:modData});
}
});
}
});
break;
default:
db.query('SELECT * FROM blog_list_table', function (err, resultData) {
if (err) {
console.error(err);
res.status(500).send({code: 500, msg: 'database error'}).end();
} else {
res.render('admin/blog.ejs', {formData: resultData});
}
});
}
});
这里的switch,主要是分为,查询,删除,和修改
都是些简单的CRUD操作,这里就不多细说了。不熟悉的兄弟们可以看一看,写的不好,多多提意见。
同理,post的请求,主要就是分为,文章列表的添加,和修改的两个post,代码如下:
router.post('/', function (req, res) {
//此处验证应该更加严格,比如正则
var title = req.body.title.trim();
var author = req.body.author.trim();
var summary = req.body.summary.trim();
var href = req.body.href.trim();
if (title && author && summary && href) {
if(req.body.modified){
db.query('UPDATE blog_list_table SET title="'+title+'",author="'+author+'",summary="'+summary+'",href="'+href+'" WHERE ID="'+req.body.modified+'"',function (err,resultData) {
if(err){
console.error(err);
res.status(500).send({code:500,msg:'database error'});
}else{
res.redirect('/admin/blog');
}
})
}else{
db.query('INSERT INTO blog_list_table (title,author,summary,href) VALUE("' + title + '","' + author + '","' + summary + '","' + href + '")', function (err, data) {
if (err) {
console.error(err);
res.status(500).send({code: 500, msg: 'database error'}).end();
} else {
res.redirect('/admin/blog');
}
});
}
} else {
res.status(400).send({code: 400, msg: 'parameters error'}).end();
}
});
##用户管理
后台的管理大概也即是这么多,用户管理和博文管理基本都是差不多的,这里重点是说下,这里用到的图片上传。
图片上传我用的是multer中间件,不知道的可以查下,注意用这个中间件接受图片上传时form表单的enctype必须要设置为multipart/form-data
关于图片上传后,默认是不包括后缀名的,所以这里我们需要用到fs模块的重命名操作,代码如下:
fs.rename(req.files[0].path, req.files[0].path + ext, function (err) {
if (err) {
console.error(err);
res.status(500).send({code: 500, msg: 'data error'});
} else {
db.query('INSERT INTO user_table (username,email,pic_header) VALUE("' + username + '","' +
email + '","' + pic_header + '")', function (err, resultData) {
if (err) {
console.error(err);
res.status(500).send({code: 500, msg: 'database error'});
} else {
res.redirect('/admin/users');
}
});
}
});
前面的变量定义主要如下:
var username = req.body.username;
var email = req.body.email;
if(req.files.length>0){
var ext = pathLib.parse(req.files[0].originalname).ext;
var pic_header = '/files/upload/' + req.files[0].filename + ext;
}
用到path模块对路径的解析。为了获取后缀名~
说到这,对于修改也就很简单了,就是删除原先有的那个图片,然后换上现在有的图片,具体代码如下:
//需要进行一些校验,这里就忽略了
if(req.body.modified){//修改
//查看有没有新传来的头像,如果有,则删除,新建,如果没有,直接更新需要更新的内容
if(req.files.length>0){
//有修改头像,则进行原来头像的删除,再上传
db.query('SELECT * FROM user_table WHERE ID="'+req.body.modified+'"',function (err,modData) {
if (err) {
console.error(err);
res.status(500).send({code: 500, msg: 'database error'});
}else if(modData.length == 0){
res.status(400).send({code: 400, msg: 'parameters error'});
}else{
fs.unlink(modData[0].pic_header.replace('\/files','static'),function (err) {
if(err){
console.error(err);
res.status(500).send({code:500,msg:'operate error'});
}else{
//删除成功,开始对新的文件进行重命名
fs.rename(req.files[0].path, req.files[0].path + ext, function (err) {
if (err) {
console.error(err);
res.status(500).send({code: 500, msg: 'operate error'});
} else {
db.query('UPDATE user_table SET username="'+
username+'",email="' + email + '",pic_header="' +
pic_header + '" WHERE ID="'+req.body.modified+'"',function (err,data) {
if (err) {
console.error(err);
res.status(500).send({code: 500, msg: 'database error'});
}else{
res.redirect('/admin/users');
}
});
}
});
}
})
}
})
}else{
db.query('UPDATE user_table SET username="'+username+'",email="' + email + '" WHERE ID="'+
req.body.modified+'"',function (err,data) {
if (err) {
console.error(err);
res.status(500).send({code: 500, msg: 'database error'});
}else{
res.redirect('/admin/users');
}
});
}
}
看到这个代码是不是感觉想死的心都有了???的确,多少篇文章都说到了关于nodejs的回调地狱,但是Node 7.6 发布了,支持了async函数,JavaScript异步的写法彻底改变了。所以这个大可不必太过于担心
况且Koa不就这么的出来和投入大范围的使用了嘛,这里我们大可用express,去尽情的感受Node的魅力。
##结束语
说到这,基本的一个小小后台管理应用就完事了,是不是感觉没有想象中的那么难?写的不好,欢迎大家吐槽指教~~~
最后,欢迎愿意一起学习nodejs的朋友加入,Nodejs技术群:209530601
~~