zoukankan      html  css  js  c++  java
  • node.js(小案例)_实现学生信息增删改

    一、前言                                                                                                  

    本节内容主要对小案例做一个总结:

                                     1、如何开始搭建小项目

                                     2、路由设计

                                     3、模块应用

                                     4、项目源码以及实现过程github地址:

    项目演示如下:

    二、主要内容                                                                                           

    1、项目的关键性js源码:

     项目的入口:     

    /**
     * app.js 入门模块
     * 职责:
     *   创建服务
     *   做一些服务相关配置
     *     模板引擎
     *     body-parser 解析表单 post 请求体
     *     提供静态资源服务
     *   挂载路由
     *   监听端口启动服务
     */
    
    var express = require('express')
    var router = require('./router')
    var bodyParser = require('body-parser')
    
    var app = express()
    
    app.use('/node_modules/', express.static('./node_modules/'))
    app.use('/public/', express.static('./public/'))
    
    app.engine('html', require('express-art-template'))
    
    // 配置模板引擎和 body-parser 一定要在 app.use(router) 挂载路由之前
    // parse application/x-www-form-urlencoded
    app.use(bodyParser.urlencoded({ extended: false }))
    // parse application/json
    app.use(bodyParser.json())
    
    // 把路由容器挂载到 app 服务中
    app.use(router)
    
    app.listen(3000, function () {
      console.log('running 3000...')
    })
    
    module.exports = app
    app.js

    路由设计模块:

    /**
     * router.js 路由模块
     * 职责:
     *   处理路由
     *   根据不同的请求方法+请求路径设置具体的请求处理函数
     * 模块职责要单一,不要乱写
     * 我们划分模块的目的就是为了增强项目代码的可维护性
     * 提升开发效率
     */
    
    var fs = require('fs')
    var Student = require('./student')
    
    // Express 提供了一种更好的方式
    // 专门用来包装路由的
    var express = require('express')
    
    // 1. 创建一个路由容器
    var router = express.Router()
    
    // 2. 把路由都挂载到 router 路由容器中
    
    /*
     * 渲染学生列表页面
     */
    router.get('/students', function (req, res) {
      Student.find(function (err, students) {
        if (err) {
          return res.status(500).send('Server error.')
        }
        res.render('index.html', {
          fruits: [
            '苹果',
            '香蕉',
            '橘子'
          ],
          students: students
        })
      })
    })
    
    /*
     * 渲染添加学生页面
     */
    router.get('/students/new', function (req, res) {
      res.render('new.html')
    })
    
    /*
     * 处理添加学生
     */
    router.post('/students/new', function (req, res) {
      // 1. 获取表单数据
      // 2. 处理
      //    将数据保存到 db.json 文件中用以持久化
      // 3. 发送响应
      Student.save(req.body, function (err) {
        if (err) {
          return res.status(500).send('Server error.')
        }
        res.redirect('/students')
      })
    })
    
    /*
     * 渲染编辑学生页面
     */
    router.get('/students/edit', function (req, res) {
      // 1. 在客户端的列表页中处理链接问题(需要有 id 参数)
      // 2. 获取要编辑的学生 id
      // 
      // 3. 渲染编辑页面
      //    根据 id 把学生信息查出来
      //    使用模板引擎渲染页面
    
      Student.findById(parseInt(req.query.id), function (err, student) {
        if (err) {
          return res.status(500).send('Server error.')
        }
        res.render('edit.html', {
          student: student
        })
      })
    })
    
    /*
     * 处理编辑学生
     */
    router.post('/students/edit', function (req, res) {
      // 1. 获取表单数据
      //    req.body
      // 2. 更新
      //    Student.updateById()
      // 3. 发送响应
      Student.updateById(req.body, function (err) {
        if (err) {
          return res.status(500).send('Server error.')
        }
        res.redirect('/students')
      })
    })
    
    /*
     * 处理删除学生
     */
    router.get('/students/delete', function (req, res) {
      // 1. 获取要删除的 id
      // 2. 根据 id 执行删除操作
      // 3. 根据操作结果发送响应数据
    
      Student.deleteById(req.query.id, function (err) {
        if (err) {
          return res.status(500).send('Server error.')
        }
        res.redirect('/students')
      })
    })
    
    // 3. 把 router 导出
    module.exports = router
    
    // 这样也不方便
    // module.exports = function (app) {
    //   app.get('/students', function (req, res) {
    //     // readFile 的第二个参数是可选的,传入 utf8 就是告诉它把读取到的文件直接按照 utf8 编码转成我们能认识的字符
    //     // 除了这样来转换之外,也可以通过 data.toString() 的方式
    //     fs.readFile('./db.json', 'utf8', function (err, data) {
    //       if (err) {
    //         return res.status(500).send('Server error.')
    //       }
    
    //       // 从文件中读取到的数据一定是字符串
    //       // 所以这里一定要手动转成对象
    //       var students = JSON.parse(data).students
    
    //       res.render('index.html', {
    //         fruits: [
    //           '苹果',
    //           '香蕉',
    //           '橘子'
    //         ],
    //         students: students
    //       })
    //     })
    //   })
    
    //   app.get('/students/new', function (req, res) {
    
    //   })
    
    //   app.get('/students/new', function (req, res) {
    
    //   })
    
    //   app.get('/students/new', function (req, res) {
    
    //   })
    
    //   app.get('/students/new', function (req, res) {
    
    //   })
    
    //   app.get('/students/new', function (req, res) {
    
    //   })
    // }
    View Code

    功能实现模块

    /**
     * student.js
     * 数据操作文件模块
     * 职责:操作文件中的数据,只处理数据,不关心业务
     *
     * 这里才是我们学习 Node 的精华部分:奥义之所在
     * 封装异步 API
     */
    
    var fs = require('fs')
    
    var dbPath = './db.json'
    
    /**
     * 获取学生列表
     * @param  {Function} callback 回调函数
     */
    exports.find = function (callback) {
      fs.readFile(dbPath, 'utf8', function (err, data) {
        if (err) {
          return callback(err)
        }
        callback(null, JSON.parse(data).students)
      })
    }
    
    /**
     * 根据 id 获取学生信息对象
     * @param  {Number}   id       学生 id
     * @param  {Function} callback 回调函数
     */
    exports.findById = function (id, callback) {
      fs.readFile(dbPath, 'utf8', function (err, data) {
        if (err) {
          return callback(err)
        }
        var students = JSON.parse(data).students
        var ret = students.find(function (item) {
          return item.id === parseInt(id)
        })
        callback(null, ret)
      })
    }
    
    /**
     * 添加保存学生
     * @param  {Object}   student  学生对象
     * @param  {Function} callback 回调函数
     */
    exports.save = function (student, callback) {
      fs.readFile(dbPath, 'utf8', function (err, data) {
        if (err) {
          return callback(err)
        }
        var students = JSON.parse(data).students
    
        // 添加 id ,唯一不重复
        student.id = students[students.length - 1].id + 1
    
        // 把用户传递的对象保存到数组中
        students.push(student)
    
        // 把对象数据转换为字符串
        var fileData = JSON.stringify({
          students: students
        })
    
        // 把字符串保存到文件中
        fs.writeFile(dbPath, fileData, function (err) {
          if (err) {
            // 错误就是把错误对象传递给它
            return callback(err)
          }
          // 成功就没错,所以错误对象是 null
          callback(null)
        })
      })
    }
    
    /**
     * 更新学生
     */
    exports.updateById = function (student, callback) {
      fs.readFile(dbPath, 'utf8', function (err, data) {
        if (err) {
          return callback(err)
        }
        var students = JSON.parse(data).students
    
        // 注意:这里记得把 id 统一转换为数字类型
        student.id = parseInt(student.id)
    
        // 你要修改谁,就需要把谁找出来
        // EcmaScript 6 中的一个数组方法:find
        // 需要接收一个函数作为参数
        // 当某个遍历项符合 item.id === student.id 条件的时候,find 会终止遍历,同时返回遍历项
        var stu = students.find(function (item) {
          return item.id === student.id
        })
    
        // 这种方式你就写死了,有 100 个难道就写 100 次吗?
        // stu.name = student.name
        // stu.age = student.age
    
        // 遍历拷贝对象
        for (var key in student) {
          stu[key] = student[key]
        }
    
        // 把对象数据转换为字符串
        var fileData = JSON.stringify({
          students: students
        })
    
        // 把字符串保存到文件中
        fs.writeFile(dbPath, fileData, function (err) {
          if (err) {
            // 错误就是把错误对象传递给它
            return callback(err)
          }
          // 成功就没错,所以错误对象是 null
          callback(null)
        })
      })
    }
    
    /**
     * 删除学生
     */
    exports.deleteById = function (id, callback) {
      fs.readFile(dbPath, 'utf8', function (err, data) {
        if (err) {
          return callback(err)
        }
        var students = JSON.parse(data).students
    
        // findIndex 方法专门用来根据条件查找元素的下标
        var deleteId = students.findIndex(function (item) {
          return item.id === parseInt(id)
        })
    
        // 根据下标从数组中删除对应的学生对象
        students.splice(deleteId, 1)
    
        // 把对象数据转换为字符串
        var fileData = JSON.stringify({
          students: students
        })
    
        // 把字符串保存到文件中
        fs.writeFile(dbPath, fileData, function (err) {
          if (err) {
            // 错误就是把错误对象传递给它
            return callback(err)
          }
          // 成功就没错,所以错误对象是 null
          callback(null)
        })
      })
    }
    student.js

    2、项目搭建以及具体实现 

                     第一步:搭建目录,用public文件夹来存放css img等静态文件,用views来存放页面

                     第二步:app.js作为整个项目的入口,先用express创建好服务器,

                     安装好express: npm install express

    //引入express
    var express = require('express');
    
    //创建
    var app = new express();
    //请求访问
    app.get('/',function (req, res){
      res.send('hello student')
    })
    //设置端口
    app.listen(3000, function () {
    
        console.log('server is running')
    })

                     第三步:配置开放静态资源 :项目中我们一般让public目录下的资源作为公共资源,可以给用户访问

    //配置开放静态资源
    app.use('/public/', express.static('./public/'))
    app.use('/node_moudles/', express.static('./node_moudles/'))

                     

                     第四步:读取数据文件将首页渲染出来,这里需要用到express-art-template包

                     安装:npm install express-art-template

                     配置如下:

    //配置express-art-template
    app.engine('html', require('express-art-template'))
    //使用模板来渲染
    app.get('/',function (req, res){
    //这里会默认到views文件下面去找index.html
      res.render('index.html', {
          fruits: [
          '苹果',
          '香蕉',
          '橘子'
          ]
      })
    })

                      第五步:读取数据,将数据渲染到页面中,先输出一下看看data里面的内容,

    data中的内容如下:发现为一串十六进制数据,

    用JSON.parse()函数可以将字符串转换为对象,如下图所示,现在我们可以拿到里面的对象了

                 第六步:需要进行路由设计,路由就相当于一张记录有所有路径信息的一张表,我们将所有要访问的路径存放在一张表中,利用node.js中的模块化思想,将每一个功能分块实现

              (1)  路由表设计如下:

    请求方法

    请求路径

    GET

    POST

    备注作用

    GET

    / students

    渲染首页

    GET

    GET

    /students/new

    /students/edit

    点击 跳到一个新的添加页面,

    跳到编辑页

    POST

    /students

    Name age gender

    点击Submit 按钮,跳到首页

    post

    /students/edit

    Id

    Name age gender

    点击“编辑”,跳到编辑那个页面

    GET

    /students/delete

    id

    操作删除

                 (2)设计好路由表之后先将路由表的壳子搭好  


    //按照上面的表格配置好路由壳子
    router.get('/', function (req, res) { }); router.get('/students/new', function (req, res){ }); router.get('/students/edit', function (req, res){ }); router.post('/students', function (req, res) { }); router.post('/students/edit', function (req, res) { }); router.get('/students/delete', function(req, res) { });

                (3)我们要在app.js中引用这个自定义的路由模块,所以需要在router.js中导出, 在app.js中引用

                 router.js中导出方法:

    //最后还要导出这个路由
    module.exprots = router;

               在app.js中引用

    //引入路由
    var router = require(./router.js)
    
    //使用路由模块
    app.use(router)

               将第五步中的请求/的一条路由放到router.js里面,测试一下是能请求成功的

               

               第七步:上面的router.js中既有get 路由的功能,又有fs查询读取文件的功能,我们也可以用模块化 的思维,将router里面的增删改功能给抽取出来放到一个新的student.js模块中,让这个新的模块实现我们需要的功能

               (1)同样先搭建好壳子,也需要导出,这里我们就用exports导出

    //查找学生信息
    exports.find = function(){}
    
    //添加保存学生信息
    exports.save = function(){}
    
    //修改更新学生信息
    exports.updateById = function(){}
    
    //删除学生信息
    exports.deleteById = function(){}

               (2)同样也需要在那边引用

    //加入student模块
    var Student = require('./student.js')
    
    
    //调用
    Student.find()

               第八步:查找实现,由于这个函数里面还用到了读取文件的操作,是异步的,所以不能用传统的函数调用的方式,要获取里面的数据需要用到回调函数来拿到数据

           (1)在student.js中写查找方法

    //可以先模拟调用写法
    //data=Student.find()由于函数中是异步的所以不能用传统的方式
    Student.find( function (data) {
      console.log(data)
    })
    exports.find = function (callback) {
    
      fs.readFile('./data.json',  function(err, data) {
    
                 if (err) {
                   return callback(err); //如果文件读取错误,就结束让回调函数处理
    
                }
    
             //文件读取成功,第一个参数为null说明成功,这里需要将得到的二进制字符串转化为对象
                callback(null, JSON.parse(data).students)
    
    })
    
    }

                (2)  router.js中调用如下:

    //请求路径为/students的时候,调用find方法,并在页面中渲染数据
    router.get('/students', function (req, res) {
       
       
       Student.find(function (err, students) {
        if(err) {
          return res.status(500).send('Server err');
        }
      
        res.render('index.html', {
          fruits: [
            '苹果',
            '香蕉',
            '橘子'
          ],
          students: students
        })
     })
    
    });

       

               第九步:

                     (1)在student.js中写save添加方法,同样也可以先模拟调用再来写这个方法,添加的时候需要将要添加的对象传进去,然后在文件中添加传进去的对象,由于data文件是一个对象,还要将数组转化为对象

    exports.save = function (student, callback ) {
    
         fs.readFile('./data.json', function (err, data) {
         if(err){
             return callback(err)  //回调函数中如何处理错误,在这里我们不用关心
         }
         
         //接受文件,这里得到的为数组
         var students = JSON.parse(data).students
         student.id = students[students.length-1].id+1;
    
         //push向数组中添加新的
         students.push(student);
    
         //执行完上面几步之后得到的是一个数组,但是在data中存入的是json格式的对象
         var fileData = JSON.stringify({
             students: students
         })
    
         //然后将文件写入data里面
         fs.writeFile('./data.json', fileData, function (err){
             if(err){
                 callback(err);
             }
    
             callback(null);
         })
    
      })
    }

                      (2)在router.js中调用:由于上面添加的过程提交的数据是以post方式提交,而express是不支持post提交的,所以要先安装一个body-parser包

                   安装:npm install body-parser

                   配置:

    //安装好后引用包
    var bodyParse = require('body-parse');
    
    //配置
    app.use(bodyParse.urlencoded({ extentend: false}))
    app.use(bodyParse.json())

                    点击按钮跳到添加页面,然后点击提交按钮,将数据提交,并且页面重新跳到首页

    router.get('/students/new', function (req, res){
        res.render('new.html')
    });
    
    router.post('/students/new', function (req, res) {
    //先打印提交的内容
        console.log(req.body);
        Student.save(req.body, function (err) {
          if(err) {
             return res.status(500).send('Server err');
          }
    //重新跳会首页
          res.redirect('/students')
        })
    
    });

            第十步:实现点击“编辑”跳到编辑页面

          (1)这里需要通过id查询到点击的那条数据,并且在编辑页中渲染

        

    //封装一个通过id请求的的函数
    
    exports.findById = function(id, callback) {
      fs.readFile('./data.json', 'utf8', function (err, data) {
        if (err) {
          return callback(err)
        }
        var students = JSON.parse(data).students
      //这个方法是es6提供的,当满足了item.id === parseInt(id),的时候会会返回整个对象
        var ret = students.find(function (item) {
            return item.id === parseInt(id);
        })
    
        callback(null, ret);
    });
    }

        

         (2)在router.js函数中get方式请求/students/edit

    /这一次通过get到那个编辑页面
    router.get('/students/edit', function (req, res) {
    
      //调用得到的是保存之后的数据
      //1.如何获取数据
      //2.处理数据
      //3.发送响应
      //4.将数据再次写入data.json文件
      Student.findById(req.query.id, function (err, student) {
        if (err) {
          return res.status(500).send('Server err');
        }
        //console.log(student);
        res.render('edit.html',{
    
          student: student
        })
      })
    
    });

           (3)更新数据,需要以post方式提交数据到/students/edit,先在student

    exports.find = function (callback){
      fs.readFile('./data.json', function (err, data) {
         if(err){
             return callback(err)  //回调函数中如何处理错误,在这里我们不用关心
         }
         //这里的err=null说明请求成功
         callback(null, JSON.parse(data).students);
    
      })
    
    
    }

             (4)router.js中调用

    router.post('/students/edit', function (req, res) {
    
     Student.updateById(req.body, function(err){
       if (err){
        return res.status(500).send('Server err');
       }
    
       res.redirect('/students')
     })
    
    });

           第十一步:实现删除,删除的时候也要通过url地址拿到删除的id,然后查询到删除的对象

    exports.deleteById = function (id, callback) {
    
         fs.readFile('./data.json','utf8', function (err, data) {
         if(err){
             return callback(err)  //回调函数中如何处理错误,在这里我们不用关心
         }
         //得到的是一个数组
         var students = JSON.parse(data).students;
    
         var deleteId = students.findIndex(function (item) {
             return item.id === parseInt(id);
         })
    
         //做删除操作
         students.splice(deleteId, 1);
    
         //删除之后吧数组转化为对象
         var fileData = JSON.stringify({
             students:students
         })
    
         fs.writeFile('./data.json', fileData, function(err){
            if(err) {
                return callback(err)
            }
    
            callback(null);
         })
     });
    
    }

       

    三、总结以及踩坑                                                                                    

    1、做项目的时候可以将一些允许用户访问的静态资源文件放到一个public文件中,开放静态文件

    2、要善于将项目中的功能分成模块化来处理

    3、req.body拿到的是二进制或者十六进制数据,要对数据进行操作必须要用JSON.parse(data)转化为对象

    4、将数组转化为对象用.

     var fileData = JSON.stringify({
             students:students
         })
    虽然现在走得很慢,但不会一直这么慢
  • 相关阅读:
    Linux
    Linux
    Linux
    Linux
    Linux
    Python
    Linux
    Python
    MySQL
    Python
  • 原文地址:https://www.cnblogs.com/xxm980617/p/10550179.html
Copyright © 2011-2022 走看看