Skip to content

💦 ejs+express+mysql实现基本的CRUD后台管理应用 💦

Notifications You must be signed in to change notification settings

Nealyang/ejs-express-mysql

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Feb 27, 2017
0e1fc42 · Feb 27, 2017

History

16 Commits
Feb 23, 2017
Feb 24, 2017
Feb 25, 2017
Feb 25, 2017
Feb 25, 2017
Feb 25, 2017
Feb 23, 2017
Feb 27, 2017
Feb 24, 2017
Feb 25, 2017
Feb 25, 2017

Repository files navigation

ejs-express-mysql

###基于express,MySQL,ejs实现的一个简单基本的网站后台管理应用

##前言 也是这两周才正式的接触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前端页面的重点代码讲解 公共头部的引入:

    <% include common/top.ejs %>

查询数据库的前端展示:

    <% for(var i = 0;i<formData.length;i++){%>
        <tr>
            <td><%=formData[i].ID%></td>
            <td><%=formData[i].title%></td>
            <td><%=formData[i].author%></td>
            <td><%=formData[i].summary%></td>
            <td><%=formData[i].href%></td>
            <td>
                <a href="?action=del&id=<%=formData[i].ID%>" onclick="return confirm('确定删除?')">
                    <button>删除</button>
                </a>
                <a href="?action=mod&id=<%=formData[i].ID%>">
                    <button>修改</button>
                </a>
            </td>
        </tr>
        <%}%>

修改后我们通过页面给的标识来知道是否为修改的提交,毕竟这里我们没有前端逻辑的js

    <%if(typeof modData != 'undefined'){%>
    <div class="add_form" style="display: flex">
        <img src="/files/admin/img/add.png">
        <div class="form_bg"></div>
        <form action="?" method="post">
            <input type="hidden" name="modified" value="<%= modData[0].ID %>">
            标题: <input type="text" name="title" autofocus="autofocus" value="<%=modData[0].title%>"><br>
            作者: <input type="text" name="author" value="<%=modData[0].author%>"><br>
            摘要: <textarea name="summary"><%=modData[0].summary%></textarea><br>
            链接: <input type="text" name="href" value="<%=modData[0].href%>"><br>
            <input type="submit" value="确认修改">
        </form>
    </div>
    <%}%>

##博文管理 还是那句话,如果是仅仅提供前端的接口的话,这里会方便很多,然后我们用的是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 ~~

About

💦 ejs+express+mysql实现基本的CRUD后台管理应用 💦

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published