飞道的博客

7、Node.js利用express框架实现学生系统crud

271人阅读  评论(0)

大家好,我是Counterrr,生命不息学习不止。

Talk is cheap, Show me the bug.

本文目录

  1. 项目效果预览;
  2. 了解crud,了解路由设计模式;
  3. 实现学生系统的增删改查功能;

1. 项目效果预览:


2、了解crud,了解路由设计模式:

crud是什么呢?其实写过后端的话对这个就不陌生,其实crud就是英文增删改查的首字母缩写。create read update delete
我们需要开发一套系统就要先设计好路由。

路由设计:
请求方法 请求路径 get参数 post参数 描述
GET / 渲染首页
GET /students/create 渲染添加学生页面
POST /students/create name(string)、age(number)、sex(string) 处理添加学生请求
GET /students/edit id(number) 渲染编辑学生页面
POST /students/edit name(string)、age(number)、sex(string) 修改编辑学生页面
GET /students/delete id(number) 删除学生信息

好的我们设计好路由后,后面的开发将会非常的清晰。


3、实现学生系统的增删改查功能:

在桌面新建文件夹express-crud,目录结构如下:

express-crud
├── database
│   └── db.json
├── public
│   └── dashbord.css
├── views
│   ├── index.html
│   ├── studentCreate.html
│   └── studentEdit.html
├── app.js
├── router.js
├── Student.js
  • database文件夹下的db.json当做是我们的数据存放位置(暂时充当数据库的角色。)
  • public文件夹是我们存放公共资源的位置,其中dashbord.css为页面部分样式。
  • views文件夹我们存放服务端需要渲染的html模板,其中index.html为首页展示学生信息的列表。studentCreate.html为新增一个学生的页面,studentEdit.html为编辑一个学生的页面。
  • app.js为入口文件。
  • router.js为路由处理文件。
  • Student.js为专门处理数据的增删改查业务的文件。

在真实开发中,都是以模块化开发的。
好的构建完目录后,我们打开vscode命令行,输入命令 npm init -y 快速生成项目说明书,接着来安装下开发依赖包,输入命令:

npm install express art-template express-art-template --save

安装完后,我们在文件中键入代码:

  1. app.js代码如下:
const express = require('express')
const router = require('./router')
const bodyParser = require('body-parser')
// 创建serve服务
const app = express()

app.use(bodyParser.urlencoded({extended:false}))
app.use(bodyParser.json())

// 开放静态资源
app.use('/public/', express.static('./public/'))
// 服务端模板引擎
app.engine('html', require('express-art-template'))

app.use(router)

// 监听端口
app.listen(3000, () => {
    console.log('serve is running on 3000 port')
})
  • 代码解读:
    1、其实这里面的代码我们之前都写过了,熟悉不过,唯一新增的一行之前为接触的代码app.use(router),这行代码其实就是去使用了router.js这个文件,其实就是使用express为我们封装好的路由容器,我们在这里面去写各个路由的状态。

  1. router.js代码如下:
const express = require('express')
const Student = require('./Student.js')

const router = express.Router()

router.get('/', (req, res) => {
    // 获取学生信息列表
    Student.read((err, array) => {
        if (err) {
            res
            .sendStatus(500)
            .send('SERVE ERROR')
        }
        else {
            // console.log(array)
            res.render('index.html', {array})
        }
    })
})

// 渲染学生添加页面
router.get('/students/create', (req, res) => {
    res.render('studentCreate.html')
})

// 添加学生信息
router.post('/students/create', (req,res) => {
	// 添加保存学生信息
    Student.save(req.body, (err) => {
        if (err) {
            res
            .sendStatus(500)
            .send('SERVE ERROR')
        }
        else {
            res.redirect('/')
        }
    })
})

// 渲染编辑学生页面
router.get('/students/edit', (req, res) => {
	// 根据学生id进行查找
    Student.editById(parseInt(req.query.id), (err, student) => {
        if (err) {
            res
            .sendStatus(500)
            .send('SERVE ERROR')
        }
        else {
            res.render('studentEdit.html', {
                student
            })
        }
    })
})

// 提交编辑学生页面
router.post('/students/edit', (req, res) => {
    // console.log(req.body)
    Student.updateById(req.body, (err) => {
        if (err) {
            res
            .sendStatus(500)
            .send('SERVE ERROR')
        }
        else {
            res.redirect('/')
        }
    })
})

// 删除学生信息
router.get('/students/delete', (req, res) => {
    Student.deleteById(req.query.id, (err) => {
        if (err) {
            res.sendStatus(500).send('SERVER ERROR')
        }
        else {
            res.redirect('/')
        }
    })
})

module.exports = router 

这块的话代码就多了,主要就是根据我们路由的设计去完成相对应的增删改查的功能。

  • 代码解读:
    1、我们这里引用了express,并且调用了它上面的Router( )方法,那这个方法就是express为我们封装好的路由容器,那我们在这上面去进行 GET、POST。。。 请求方法更好。
    2、引入了Student.js 那涉及到读取文件然后学生数据的增删改查的逻辑都放在这个js文件里去写,那路由的文件,我们专注于写路由,这样条理更清晰。代码也更好维护。
    3、那剩下的语法我们之前也熟悉了,res.render( )去渲染模板并且返回给客户端。res.redirect('/') 为临时重定向到首页。res.sendStatus(500).send('SERVER ERROR') 发生错误给客户端返回状态码,并且提示错误。
    4、那剩下的Student.read( ) 、Student.save( ) 、 Student.editById( )、Student.updateById( )、 Student.deleteById( ) 我们在Student.js这个文件中去解读。

  1. Student.js的代码如下:
const fs = require('fs')
const path = './database/db.json'

//查询所有学生信息
exports.read = (callback) => {
    fs.readFile(path, 'utf8', (err, data) => {
        if (err) {
           return callback(err)
        }
        else {
           return callback(null, JSON.parse(data).students)
        }
    })
}

// 保存学生信息
exports.save = (stuObj, callback) => {
    fs.readFile(path, 'utf8', (err, data) => {
        if (err) {
           return callback(err)
        }
        else {
            let students = JSON.parse(data).students
            stuObj.id = parseInt(students[students.length - 1].id) + 1
            stuObj.sex = parseInt(stuObj.sex)
            students.push(stuObj)
            newData = {
                students: students
            }
            fs.writeFile(path, JSON.stringify(newData), (err) => {
                if (err) {
                   return callback(err)
                }
                else {
                   return callback(null)
                }
            })
        }
    })
}

// 更改学生信息
exports.updateById = (stuObj, callback) => {
    fs.readFile(path, 'utf8', (err, data) => {
        if (err) {
            return callback(err)
        }
        else {
            let students = JSON.parse(data).students
            let newStuObj = students.find((item) => {
                return item.id == stuObj.id
            })
            for(let i in stuObj) {
                newStuObj[i] = stuObj[i]
            }
            students = {
                "students": students
            }
            fs.writeFile(path, JSON.stringify(students), (err) => {
                if (err) {
                   return callback(err)
                }
                else {
                   return callback(null)
                }
            })
        }
    })
}

// 编辑学生信息界面
exports.editById = (id, callback) => {
    fs.readFile(path, 'utf8', (err, data) => {
        if (err) {
           return callback(err)
        }
        else {
           let students = JSON.parse(data).students
           let result = students.find((i) => i.id == id)
           callback(null, result)
        }
    })
}

// 删除学生信息
exports.deleteById = (id, callback) => {
    fs.readFile(path, 'utf8', (err, data) => {
        if (err) {
           return callback(err)
        }
        else {
            let students = JSON.parse(data).students
            let index = students.findIndex((item) => {
                return item.id == id
            })
            students.splice(index, 1)
            students = {
                students
            }
            fs.writeFile(path, JSON.stringify(students), (err) => {
                callback(err)
            })
        }
    })
}
  • 代码解读:
    1、可以看到我们这边主要引入了读写文件操作的核心包 fs ,这个在之前的小项目中也操作了。那可以看到我们这边每个方法都接收一个函数作为参数,回调函数,因为对./database/db.json 这个文件的增删改查都是异步的,所以这边得用回调函数处理的方式,在es6中有promise解决的方法和async await 的方法,但是都是基于回调函数,所以我们有必要自己了解下这个,等到后面就可以用es6的方式去解决,好的剩下的都是基于数组的操作,这个应该不难,等到后期接触了数据,那更简单了,不用我们再去操作文件了,直接一个api的事情,这边简单了解底层实现的原理。

  1. db.json 代码如下:
{
    "students": [
        {
            "id": "1",
            "name": "张三",
            "age": "22",
            "sex": "1"
        },
        {
            "id": 2,
            "name": "小花",
            "age": 21,
            "sex": 1
        },
        {
            "name": "小芳",
            "age": "29",
            "sex": 0,
            "id": 3
        },
        {
            "name": "王五",
            "age": "18",
            "sex": 0,
            "id": 4
        }
    ]
}

简单的写死几个数据。


  1. index.html 代码如下:
<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <title>Dashboard Template for Bootstrap</title>

    <!-- Bootstrap core CSS -->
    <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
    <link href="/public/dashbord.css" rel="stylesheet">
  </head>

  <body>

    <nav class="navbar navbar-inverse navbar-fixed-top">
      <div class="container-fluid">
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
          <a class="navbar-brand" href="#">学生管理系统</a>
        </div>
      </div>
    </nav>

    <div class="container-fluid">
      <div class="row">
        <div class="col-sm-3 col-md-2 sidebar">
          <ul class="nav nav-sidebar">
            <li class="active"><a href="#">学生信息<span class="sr-only">(current)</span></a></li>
          </ul>
        </div>
        <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
          <h1 class="page-header">控制台</h1>
            <a href="/students/create" class="btn btn-primary" role="button">添加学生信息</a>
          <div class="table-responsive">
            <table class="table table-striped">
              <thead>
                <tr>
                  <th>学号</th>
                  <th>姓名</th>
                  <th>年龄</th>
                  <th>性别</th>
                  <th>操作</th>
                </tr>
              </thead>
              <tbody>
                {{each array}}
                <tr>
                  <td>{{$value.id}}</td>
                  <td>{{$value.name}}</td>
                  <td>{{$value.age}}</td>
                  <td>{{$value.sex ? '女' : '男'}}</td>
                  <td>
                    <a href="/students/edit?id={{$value.id}}">编辑</a>
                    <a href="/students/delete?id={{$value.id}}">删除</a>
                  </td>
                </tr>
                {{/each}}
              </tbody>
            </table>
          </div>
        </div>
      </div>
    </div>
  </body>
</html>


主要用了bootstrap的样式和模板。


  1. studentCreate.html 代码如下:
<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <title>Dashboard Template for Bootstrap</title>

    <!-- Bootstrap core CSS -->
    <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
    <link href="/public/dashbord.css" rel="stylesheet">
</head>

<body>

    <nav class="navbar navbar-inverse navbar-fixed-top">
        <div class="container-fluid">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar"
                    aria-expanded="false" aria-controls="navbar">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a class="navbar-brand" href="#">学生管理系统</a>
            </div>
        </div>
    </nav>

    <div class="container-fluid">
        <div class="row">
            <div class="col-sm-3 col-md-2 sidebar">
                <ul class="nav nav-sidebar">
                    <li class="active"><a href="#">学生信息 <span class="sr-only">(current)</span></a></li>
                </ul>
            </div>
            <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
                <h1 class="page-header">添加学生信息</h1>
                <form action="/students/create" method="POST">
                    <div class="form-group">
                        <label for="exampleInputPassword1">姓名</label>
                        <input type="text" name="name" class="form-control" placeholder="请输入姓名">
                    </div>
                    <div class="form-group">
                        <label for="exampleInputPassword1">年龄</label>
                        <input type="number" name="age" class="form-control" placeholder="请输入年龄">
                    </div>
                    <div class="radio">
                        <label>
                            <input type="radio" name="sex" value="0"></label>
                    </div>
                    <div class="radio">
                        <label>
                            <input type="radio" name="sex" value="1"></label>
                    </div>
                    <button type="submit" class="btn btn-default">确定</button>
                </form>
            </div>
        </div>
    </div>
</body>

</html>

  1. studentEdit.html 代码如下:
<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <title>Dashboard Template for Bootstrap</title>

    <!-- Bootstrap core CSS -->
    <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
    <link href="/public/dashbord.css" rel="stylesheet">
</head>

<body>

    <nav class="navbar navbar-inverse navbar-fixed-top">
        <div class="container-fluid">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar"
                    aria-expanded="false" aria-controls="navbar">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a class="navbar-brand" href="#">学生管理系统</a>
            </div>
        </div>
    </nav>

    <div class="container-fluid">
        <div class="row">
            <div class="col-sm-3 col-md-2 sidebar">
                <ul class="nav nav-sidebar">
                    <li class="active"><a href="#">学生信息 <span class="sr-only">(current)</span></a></li>
                </ul>
            </div>
            <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
                <h1 class="page-header">编辑学生信息</h1>
                <form action="/students/edit" method="POST">
                    <input type="hidden" value="{{student.id}}" name="id">
                    <div class="form-group">
                        <label for="exampleInputPassword1">姓名</label>
                        <input required type="text" name="name" class="form-control" placeholder="请输入姓名" value="{{student.name}}">
                    </div>
                    <div class="form-group">
                        <label for="exampleInputPassword1">年龄</label>
                        <input required type="number" name="age" class="form-control" placeholder="请输入年龄" value="{{student.age}}">
                    </div>
                    <div class="radio">
                        <label>
                            <input type="radio" name="sex" {{ student.sex ? '' : 'checked' }} value="0"></label>
                    </div>
                    <div class="radio">
                        <label>
                            <input type="radio" name="sex" {{ student.sex ? 'checked' : '' }} value="1"></label>
                    </div>
                    <button type="submit" class="btn btn-default">确定</button>
                </form>
            </div>
        </div>
    </div>
</body>
</html>

  1. dashbord.css 代码如下:
/*
 * Base structure
 */

/* Move down content because we have a fixed navbar that is 50px tall */
body {
    padding-top: 50px;
  }
  
  
  /*
   * Global add-ons
   */
  
  .sub-header {
    padding-bottom: 10px;
    border-bottom: 1px solid #eee;
  }
  
  /*
   * Top navigation
   * Hide default border to remove 1px line.
   */
  .navbar-fixed-top {
    border: 0;
  }
  
  /*
   * Sidebar
   */
  
  /* Hide for mobile, show later */
  .sidebar {
    display: none;
  }
  @media (min-width: 768px) {
    .sidebar {
      position: fixed;
      top: 51px;
      bottom: 0;
      left: 0;
      z-index: 1000;
      display: block;
      padding: 20px;
      overflow-x: hidden;
      overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
      background-color: #f5f5f5;
      border-right: 1px solid #eee;
    }
  }
  
  /* Sidebar navigation */
  .nav-sidebar {
    margin-right: -21px; /* 20px padding + 1px border */
    margin-bottom: 20px;
    margin-left: -20px;
  }
  .nav-sidebar > li > a {
    padding-right: 20px;
    padding-left: 20px;
  }
  .nav-sidebar > .active > a,
  .nav-sidebar > .active > a:hover,
  .nav-sidebar > .active > a:focus {
    color: #fff;
    background-color: #428bca;
  }
  
  
  /*
   * Main content
   */
  
  .main {
    padding: 20px;
  }
  @media (min-width: 768px) {
    .main {
      padding-right: 40px;
      padding-left: 40px;
    }
  }
  .main .page-header {
    margin-top: 0;
  }
  
  
  /*
   * Placeholder dashboard ideas
   */
  
  .placeholders {
    margin-bottom: 30px;
    text-align: center;
  }
  .placeholders h4 {
    margin-bottom: 0;
  }
  .placeholder {
    margin-bottom: 20px;
  }
  .placeholder img {
    display: inline-block;
    border-radius: 50%;
  }

转载:https://blog.csdn.net/weixin_44103733/article/details/105949334
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场