Node.js MVC模式+MongoDB实现学员管理系统
目录结构:
项目入口文件
/* Author:张波 */ /* 文件说明: 此文件是本项目的入口文件 启动这个项目,会先执行本文件中的代码 */ // 1. 引入模块 const http = require('http'); const fs = require('fs'); const path = require('path'); const urlmodel = require('url'); const template = require('art-template'); // 引入自定义模块 const bindRender = require('./bindRender'); const router = require('./router'); // 创建Web服务器 var server = http.createServer(); // 监听服务器端口 server.listen(4444, ()=>{ console.log("Node.js Web server running at: http://127.0.0.1:4444"); }) // 监听服务器请求 server.on('request', (req,res)=>{ bindRender(res); // 调用路由模块 router(req,res); })
拓展了res方法
// 引入需要的模块 const path = require('path'); const template = require('art-template'); // 给res这个响应对象拓展一个方法 function bindRender(res){ res.render = function(fileName, objData){ var htmlStr = template(path.join(__dirname, "views/" + fileName + ".html"), objData); this.end(htmlStr); } res.json = function(obj){ this.end(JSON.stringify(obj)) } } // 向外暴露出去 module.exports = bindRender;
路由模块
const path = require('path'); const urlmodel = require('url'); const fs = require('fs'); // 引入自定义模块 const herosController = require('./herosController'); function router(req, res) { var url = urlmodel.parse(req.url, true); var pathname = url.pathname; var query = url.query; // 获取浏览器客户端的请求方式 var method = req.method; // 根据请求的地址进行判断,响应对应的页面 if (method == "GET" && (pathname == "/" || pathname == "/index")) { //调用方法,执行代码 herosController.showIndexPage(req,res); } else if (method == "GET" && pathname == "/add") { // 读取add.html页面响应给浏览器 herosController.showAddPage(req,res); } else if (method == "GET" && pathname == "/info") { // 读取info.html页面响应给浏览器 herosController.showInfoPage(req,res); } else if (method == "GET" && pathname == "/edit") { // 读取edit.html页面响应给浏览器 herosController.showEditPage(req,res); }else if(method == "POST"&&pathname =='/addHero'){ // 调用控制器中的方法 herosController.addHero(req,res); }else if(method == "GET" &&pathname == "/delHero"){ // 调用控制器中的删除方法 herosController.delHeroById(req,res); // 加载静态资源 }else if(method == 'POST' && pathname == '/updateHero'){ herosController.updateHeroById(req,res); }else if (method == "GET" && pathname.startsWith('/node_modules')) { herosController.loadStaticResource(req,res); } else { res.end("404 Not Found!"); } } // 向外暴露router module.exports = router;
控制器模块
/** * 1.此文件是一个控制器模块 * 2.在此模块当中是专门用来书写业务逻辑的 * 3.比如说,响应给浏览器主要数据(html页面,静态文件,普通) * 4.再比如,接收浏览器传递过来的数据进行处理 * 5.总而言之,复杂的业务逻辑要写在这个模块当中 * * 在前后端的请求交互当中,req,res是最最重要的两个对象,几乎无时无刻都得使用,因此使用的时候,最好是成对出现 */ // 1. 引入需要的模块 const path = require('path') const fs = require('fs') const url = require('url') const querystring = require('querystring');// 这个内置核心模块会将post过来的字符串转换成对象 const moment = require('moment') const herosModel = require('./herosModel') // 向外暴露数据 module.exports = { showIndexPage (req,res) { herosModel.getAllHeros((err,r)=>{ // 一定要通过回调函数的方式来获取异步方法中的数据 if(err) return res.end('404') // var arr = JSON.parse(r); // 向浏览器渲染数据 // console.log(r); // console.log(typeof r); res.render('index', {"data":r}) // 响应给浏览器对应的页面 }) }, showAddPage (req,res) { res.render('add', {}) }, showInfoPage: function (req,res) { // 1. 将传递过来的路径进行进一步的解析 var urlObj = url.parse(req.url, true); // 获取传过来的ID var id = urlObj.query.id; // 根据ID获取那一条英雄对象数据 herosModel.getHeroById(id,(err,r)=>{ if(err) return res.end('404') // 如果查询成功就渲染页面 res.render('info', r[0]) }) }, showEditPage: function (req,res) { // 1.对请求路径进行解析 var urlObj = url.parse(req.url, true); // 获取传递过来的id var id = urlObj.query.id; herosModel.getHeroById(id,(err,hero)=>{ if(err) return res.end('404'); res.render('edit',hero[0]); }) }, loadStaticResource(req,res){ // 读取对应的css文件响应给浏览器 var urlObj = url.parse(req.url,true) let pathname = urlObj.pathname fs.readFile(path.join(__dirname, pathname), 'utf-8', (err, data) => { // if(err) return console.log(err.message); if (err) return res.end('not found this file') if (pathname.endsWith('css')) { // 如果路径是以css结尾,说明请求的是css文件,需要加上响应头,否则不加响应头 res.writeHeader(200, { 'Content-Type': 'text/css;charset=utf-8' }) } res.end(data) }) }, addHero(req,res){ // 1. 接收前端传递过来的数据 var str = '';// 这个字符中是用来接收post过来的请求体数据 req.on('data',chunk=>{ // 给req注册一个data事件,每当前端页面有数据发送来,就会立即触发这个事件 // chunk块的意思,也就是说数据是分块传递的,一块一块又一块的方式来传递 // 每发送一点数据,都会触发一下data事件 str += chunk; // 将接收到的数据累加起来 }) req.on('end',()=>{ // 当end事件被触发的时候,表示数据已经接收完毕 var hero = querystring.parse(str) // 把post发送过来的字符串参数转换成对象 hero.time = moment().format('YYYY-MM-DD hh:mm:ss'); herosModel.getAllHeros((err,data)=>{ if(err) return console.log(err.message); //var arr = JSON.parse(data); 将读取到的字符串转换成数组对象 hero.id = Number(data[data.length-1].id) + 1 // 接下来应该将hero这个数据传递给model模块,让model模块写入dada.json中 herosModel.addHero(hero,result=>{ // 如果添加失败,则返回给浏览器一个回执信息 if (result) return res.end(JSON.stringify({ "code": 1, "msg": "添加失败" })) // 如果添加成功,也需要返回给浏览器一个回执信息 res.end(JSON.stringify({ "code": 0, "msg": "添加成功" })) }) }) }) }, delHeroById(req,res){ var urlObj = url.parse(req.url,true) // 获取传递过来的ID var id = urlObj.query.id; // 调用model中的方法来根据id删除对应的英雄数据 herosModel.delHeroById(id,result=>{ if(result) return res.end(JSON.stringify({ "code": 1, "msg": "删除失败" })) res.end(JSON.stringify({ "code": 0, "msg": "删除成功" })) }) }, updateHeroById(req,res){ // 1.接收浏览器端post过来的数据 var str = ''; req.on('data', chunk=>{ // post请求是通过请求体一块一块的发送给服务器的,每次发送一快,就会触发一次data事件,被chunk接收一次 str += chunk; }) req.on('end', ()=>{ // 当end事件结束的时候,说明已经全部收到了发送过来的数据 var hero = querystring.parse(str); // 将字符串转换成对象 hero.time = moment().format('YYYY-MM-DD hh:mm:ss') // 添加一个time属性 // 2.调用model中的方法实现更新 herosModel.updateHeroById(hero,result=>{ if(result) res.end(JSON.stringify({ "code":1, "msg":"更新失败" })) res.json({ "code":0, "msg":"更新成功" }) }) }) } }
数据库增删改查模块
/* 1.此模块是一个model模块 2.此模块主要是用来操作数据的,比如说数据的CRUD 3.只能用这个模块来操作数据库中的数据 */ // 引入对应的模块 const fs = require('fs'); const path = require('path'); // 引入Mongodb模块 const MongoClient = require('mongodb').MongoClient; //设置数据库地址和端口 var dburl = "mongodb://127.0.0.1:27017/heros"; // 直接向外暴露对应的数据 module.exports = { // 查询数据库信息并返回给控制器方法 getAllHeros(callback) { // 连接Database MongoClient.connect(dburl, function (err, db) { if (err) return console.log("[!]连接数据库失败!") console.log("[+]MongoDB连接成功!") db.collection("heros").find().toArray(function (err, r) { // 回调函数 if (err) return callback(err); // 如果查询失败,就将错误对象返回给给控制器 // 如果读取成功,则将r返回给控制器 callback(null, r); db.close(); }) }) }, addHero(hero, callback) { this.getAllHeros((err, data) => { if (err) return callback(err); // 因为查询数据库得到的结果就是一个数组对象,所以不需要转换 data.push(hero); // 连接Database MongoClient.connect(dburl, function (err, db) { console.log(dburl); if (err) return console.log("[!]连接数据库失败!") console.log("[+]MongoDB连接成功!") db.collection("heros").insertOne({ "id": Number(hero.id), "name": hero.name, "age": hero.age, "phone": hero.phone, "address": hero.address, "gender": hero.gender, "time": hero.time }, (function (err, r) { // 回调函数 if (err) return callback(err); // 如果查询失败,就将错误对象返回给给控制器 // 如果读取成功,则将r返回给控制器 callback(null, r); db.close(); })) }) }) }, getHeroById(id, callback) { // 连接Database MongoClient.connect(dburl, function (err, db) { if (err) return console.log("[!]连接数据库失败!") console.log("[+]MongoDB连接成功!") db.collection("heros").find({ "id": Number(id) }).toArray(function (err, r) { // 回调函数 if (err) return callback(err); // 如果查询失败,就将错误对象返回给给控制器 // 如果读取成功,则将r返回给控制器 callback(null, r); db.close(); }) }) }, delHeroById(id, callback) { // 连接Database MongoClient.connect(dburl, function (err, db) { if (err) return console.log("[!]连接数据库失败!") console.log("[+]MongoDB连接成功!") db.collection("heros").deleteOne({ "id": Number(id) }, function (err, r) { // 回调函数 if (err) return callback(err); // 如果查询失败,就将错误对象返回给给控制器 // 如果读取成功,则将r返回给控制器 callback(null, r); db.close(); }) }) }, updateHeroById(hero, callback) { console.log("俺来了..."); // 找到数据库中存在的数据ID,改一下其他属性即可. var id = hero.id; console.log(id); console.log(typeof id); // 连接Database MongoClient.connect(dburl, function (err, db) { if (err) return console.log("[!]连接数据库失败!") db.collection("heros").updateOne({"id": Number(id)}, {$set:{"name":hero.name, "age":hero.age, "phone":hero.phone, "address":hero.address, "gender":hero.gender, "time":hero.time}},function (err, r) { // 回调函数 if (err) return callback(err); // 如果查询失败,就将错误对象返回给给控制器 // 如果读取成功,则将r返回给控制器 callback(null, r); db.close(); }) }) } }
页面代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Hero - Admin</title> <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.css"> </head> <body> <header> <div class="page-header container"> <h1>MongoDB<small> 学员管理系统</small></h1> </div> </header> <div class="container hero-list"> <a class="btn btn-success pull-right" href="/add">添加学员</a> <table class="table table-hover"> <thead> <th>学员编号</th> <th>学员名称</th> <th>学员性别</th> <th>学员年龄</th> <th>学员手机号</th> <th>创建日期</th> <th>操作</th> </thead> <tbody> {{each data val index}} <tr> <td>{{val.id}}</td> <td>{{val.name}}</td> <td>{{val.gender}}</td> <td>{{val.age}}</td> <td>{{val.phone}}</td> <td>{{val.time}}</td> <td> <a href="/info?id={{val.id}}">查看</a> <a href="/edit?id={{val.id}}">编辑</a> <a class="delHero" href="javascript:;" data-id="{{val.id}}">删除</a> </td> </tr> {{/each}} </tbody> </table> </div> <script src="/node_modules/jquery/dist/jquery.js"></script> <script> $(".delHero").on('click', function(){ // 发送ajax请求 $.ajax({ type:"get", url:"/delHero", data:{ id:$(this).data("id") }, dataType:"json", success:function(res){ if(res.code==0){ location.reload(true); //不要用缓存来刷新,要重新发送请求,用最新的数据刷新页面 } } }) }) </script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Hero - Admin</title> <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.css"> </head> <body> <header> <div class="page-header container"> <h1><a href="/">MongoDB</a> <small>学员详细信息</small></h1> </div> </header> <div class="container hero-list"> <p><strong>Id:</strong>{{id}}</p> <p><strong>姓名: </strong>{{name}}</p> <p><strong>性别: </strong>{{gender}}</p> <p><strong>年龄: </strong>{{age}}</p> <p><strong>手机号码: </strong>{{phone}}</p> <p><strong>家庭住址: </strong>{{address}}</p> </div> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Hero - Admin</title> <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.css"> </head> <body> <header> <div class="page-header container"> <h1><a href="/">MongoDB</a> <small>修改学员信息</small></h1> </div> </header> <div class="container hero-list"> <form id="myForm"> <input type="hidden" name="id" value="{{id}}"> <div class="form-group"> <label class="col-sm-2 control-label">学员名称</label> <div class="col-sm-10"> <input type="text" name="name" class="form-control" value="{{name}}"> </div> </div> <div class="form-group"> <label class="col-sm-2 control-label">学员年龄</label> <div class="col-sm-10"> <input type="text" name="age" class="form-control" value="{{age}}"> </div> </div> <div class="form-group"> <label class="col-sm-2 control-label">学员手机号</label> <div class="col-sm-10"> <input type="text" name="phone" class="form-control" value="{{phone}}"> </div> </div> <div class="form-group"> <label class="col-sm-2 control-label">学员住址</label> <div class="col-sm-10"> <input type="text" name="address" class="form-control" value="{{address}}"> </div> </div> <div class="form-group"> <label class="col-sm-2 control-label">性别</label> <div class="col-sm-10"> <label class="radio-inline"> <input type="radio" value="男" name="gender" {{if gender=='男'}} checked {{/if}}> 男 </label> <label class="radio-inline"> <input type="radio" value="女" name="gender" {{if gender=='女'}} checked {{/if}}> 女 </label> </div> </div> <div class="form-group"> <div class="col-sm-offset-2 col-sm-10"> <button type="submit" class="btn btn-default btn-success">保存修改</button> </div> </div> </form> </div> <script src="/node_modules/jquery/dist/jquery.js"></script> <script> // 1. 给form标签注册submit事件 $('#myForm').on('submit',function(event){ // 2. 阻止默认提交行为 因为我们当前是使用异步ajax的方式来提交数据 event.preventDefault(); // 3. 发送ajax请求 $.ajax({ type:'post', url:'/updateHero', data:$(this).serialize(), // 可以一次性获取form标签中具有name属性的input/select标签的值,并拼接成字符串 dataType:'json', // 将接收到的服务器端的json形式的字符串转换成json对象 success:function(res){ console.log(res); console.log(typeof res); if(res.code==0){ // 跳转到主页面index.html console.log("数据接收成功!") location.href='/' // 当向服务器端发送请求的时候,/前面的http://127.0.0.1:3000浏览器会自动帮我们给拼上 // 虽然我们写的是 location.href='/',但是浏览器真正的请求路径是:http://127.0.0.1:3000/ } } }) }) </script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Hero - Admin</title> <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.css"> </head> <body> <header> <div class="page-header container"> <h1><a href="/">MongoDB</a> <small>添加学员</small></h1> </div> </header> <div class="container hero-list"> <form id="myForm"> <div class="form-group"> <label>学员姓名</label> <input type="text" name="name" class="form-control" placeholder="请输入学员名称"> </div> <div class="form-group"> <label>学员年龄</label> <input type="text" name="age" class="form-control" placeholder="请输入学员年龄"> </div> <div class="form-group"> <label>学员手机号</label> <input type="text" name="phone" class="form-control" placeholder="请输入学员手机号"> </div> <div class="form-group"> <label>学员住址</label> <input type="text" name="address" class="form-control" placeholder="请输入学员住址"> </div> <div class="form-group"> <label>学员性别</label> <div class="radio"> <label> <input type="radio" name="gender" value="男" checked>男 </label> <label> <input type="radio" name="gender" value="女">女 </label> </div> </div> <button type="submit" class="btn btn-success">点击保存</button> </form> </div> <script src="/node_modules/jquery/dist/jquery.js"></script> <script> // 给form标签注册submit事件 $('#myForm').on('submit', function(event){ // 阻止form标签的默认行为 event.preventDefault(); // 发送ajax请求 $.ajax({ type:'post', url:'/addHero', data:$(this).serialize(), // 将form标签中具有name属性的input/select标签的值一次性拼接成'键=值&键=值'的字符交给异步去发送 dataType:'json', success:function(res){ if(res.code == 0){ location.href = '/'; // 跳转到首页 } } }) }) </script> </body> </html>
项目效果: