05.Express起步——案例

1,123 阅读5分钟

1.案例

通过创建一个简单的CURD接口服务,从而掌握Express的基本用法。 需求:实现对任务清单的CURD接口服务。

  • 查询任务列表
    • GET /todos
  • 根据ID查询单个任务
    • GET /todos/:id
  • 添加任务
    • POST /todos
  • 修改任务
    • PATCH /todos/:id
  • 删除任务
    • DELETE /todos/:id

2.路由设计

路由设计代码,如下:

const express = require('express');
const app = express()

// 查询任务
app.get('/todos', (req, res) => {
    res.send('get /todos')
})
// 查询单个任务
app.get('/todos/:id', (req, res) => {
    //通过req.params来获取动态的路由地址
    res.send(`get /todos/${req.params.id}`)
})
// 添加任务
app.post('/todos', (req, res) => {
    res.send('post /todos')
})
// 修改任务
app.patch('/todos/:id', (req, res) => {
    res.send(`patch /todos/${req.params.id}`)
})
// 删除任务
app.delete('/todos/:id', (req, res) => {
    res.send(`delete /todos/${req.params.id}`)
})

app.listen(3000, () => {
    console.log('server is running!!')
})

3.查询任务

3.1 新建一个db.json文件,用来存放数据,内容如下:

{
    "todos": [
        {
            "id": 1,
            "name": "降龙十八掌"
        },
        {
            "id": 2,
            "name": "独孤九剑"
        },
        {
            "id": 3,
            "name": "九阳神功"
        }
    ],
    "users": []
}

3.2 修改查询任务的代码,如下

const express = require('express');
const app = express()
const fs = require('fs')

// 查询任务
app.get('/todos', (req, res) => {
    fs.readFile('db.json', 'utf-8', (err, data) => {
        if (err) {
            return res.status(500).json({
                error: err.message
            })
        }
        var db = JSON.parse(data)
        res.status(200).json(db.todos)
    })
})
// 查询单个任务
app.get('/todos/:id', (req, res) => {
    res.send(`get /todos/${req.params.id}`)
})
// 添加任务
app.post('/todos', (req, res) => {
    res.send('post /todos')
})
// 修改任务
app.patch('/todos/:id', (req, res) => {
    res.send(`patch /todos/${req.params.id}`)
})
// 删除任务
app.delete('/todos/:id', (req, res) => {
    res.send(`delete /todos/${req.params.id}`)
})

app.listen(3000, () => {
    console.log('server is running!!')
})

思路:通过Nodejs的读文件功能,读取文件,返回的是字符串格式(注意设置成utf-8),然后,将json字符串转成json格式返给客户端。

4.查询单个数据

查询单个任务的代码,如下:

const express = require('express');
const app = express()
const fs = require('fs')

// 查询任务
app.get('/todos', (req, res) => {
    fs.readFile('db.json', 'utf-8', (err, data) => {
        if (err) {
            return res.status(500).json({
                error: err.message
            })
        }
        var db = JSON.parse(data)
        res.status(200).json(db.todos)
    })
})
// 查询单个任务
app.get('/todos/:id', (req, res) => {
    fs.readFile('db.json', 'utf-8', (err, data) => {
        if (err) {
            return res.status(500).json({
                error: err.message
            })
        }
        var db = JSON.parse(data)
        var todo = db.todos.find(todo => todo.id.toString() === req.params.id);
        if (!todo) {
            return res.status(404).json({
                data: 'not found'
            })
        }
        res.status(200).json(todo)
    })
})
// 添加任务
app.post('/todos', (req, res) => {
    res.send('post /todos')
})
// 修改任务
app.patch('/todos/:id', (req, res) => {
    res.send(`patch /todos/${req.params.id}`)
})
// 删除任务
app.delete('/todos/:id', (req, res) => {
    res.send(`delete /todos/${req.params.id}`)
})

app.listen(3000, () => {
    console.log('server is running!!')
})

思路:和查询全部数据的思路一样,查到全部数据之后,根据传过来的id和读取到的数据进行比较,这里要注意地址栏获取的参数是字符串类型,json数据中的id是number类型,需要转换以下,然后,判断以下是否查到了数据,如果查到了返回该数据,如果没查到返回404,not fount

5.封装db模块

思考:考虑到目前查询全部数据的功能和查询单条数据的功能中,获取数据的代码都一样,我们可以对他们进行封装以下。

新建db.js,内容如下:

const fs = require('fs');
const { promisify } = require('util');
const path = require('path')

const readFile = promisify(fs.readFile)
const dbPath = path.join(__dirname, './db.json')
exports.getDb = async () => {
    let data = await readFile(dbPath, 'utf8')
    return JSON.parse(data)
}
  • promisify: 将异步回调的方法处理成promise

index.js文件,代码修改如下:

const express = require('express');
const app = express()
const fs = require('fs')
const { getDb } = require('./db')

// 查询任务
app.get('/todos', async (req, res) => {
    try {
        let db = await getDb();
        res.status(200).json(db.todos);
    } catch (err) {
        res.status(500).json({
            error: err.message
        })
    }
    // fs.readFile('db.json', 'utf-8', (err, data) => {
    //     if (err) {
    //         return res.status(500).json({
    //             error: err.message
    //         })
    //     }
    //     var db = JSON.parse(data)
    //     res.status(200).json(db.todos)
    // })
})
// 查询单个任务
app.get('/todos/:id', async (req, res) => {
    try {
        let db = await getDb();
        let todo = db.todos.find(todo => todo.id.toString() === req.params.id);
        if (!todo) {
            return res.status(404).json({
                data: 'not found'
            })
        }
        res.status(200).json(todo)
    } catch {
        res.status(500).json({
            error: err.message
        })
    }
    // fs.readFile('db.json', 'utf-8', (err, data) => {
    //     if (err) {
    //         return res.status(500).json({
    //             error: err.message
    //         })
    //     }
    //     var db = JSON.parse(data)
    //     var todo = db.todos.find(todo => todo.id.toString() === req.params.id);
    //     if (!todo) {
    //         return res.status(404).json({
    //             data: 'not found'
    //         })
    //     }
    //     res.status(200).json(todo)
    // })
})
// 添加任务
app.post('/todos', (req, res) => {
    res.send('post /todos')
})
// 修改任务
app.patch('/todos/:id', (req, res) => {
    res.send(`patch /todos/${req.params.id}`)
})
// 删除任务
app.delete('/todos/:id', (req, res) => {
    res.send(`delete /todos/${req.params.id}`)
})

app.listen(3000, () => {
    console.log('server is running!!')
})

通过async和await来获取到异步的数据,然后,通过try..Catch..来捕获异常信息

6.添加数据

添加数据步骤

  • 1.获取请求体数据
    • 获取请求体数据需要提前配置解析表单请求体,express.json(),可以解析application/json格式的数据,express.urlencoded(),可以解析application/x-www-form-urlencoded格式的数据,否则,通过
  • 2.验证数据
  • 3.验证通过,将数据保存在db中

代码如下:

const express = require('express');
const app = express()
const fs = require('fs')
const { getDb } = require('./db')
const { saveDb } = require('./db')
//配置解析表单请求体: ‘application/json’
app.use(express.json())

//配置解析表单请求体:‘application/x-www-form-urlencoded’
app.use(express.urlencoded())

// 查询任务
app.get('/todos', async (req, res) => {
    try {
        let db = await getDb();
        res.status(200).json(db.todos);
    } catch (err) {
        res.status(500).json({
            error: err.message
        })
    }
})
// 查询单个任务
app.get('/todos/:id', async (req, res) => {
    try {
        let db = await getDb();
        let todo = db.todos.find(todo => todo.id.toString() === req.params.id);
        if (!todo) {
            return res.status(404).json({
                data: 'not found'
            })
        }
        res.status(200).json(todo)
    } catch {
        res.status(500).json({
            error: err.message
        })
    }
})
// 添加任务
app.post('/todos', async (req, res) => {
    try {
        // 1. 获取请求体数据
        const todo = req.body;

        // 2. 验证数据
        if (!todo.name) {
            return res.status(422).json({
                error: 'The file name is required!!'
            })
        }
        // 3。验证通过,将数据存储到db中
        const db = await getDb();
        const lastTodo = db.todos[db.todos.length - 1];
        todo.id = lastTodo ? lastTodo.id + 1 : 1;
        db.todos.push(todo)
        await saveDb(db)
        res.status(200).json(todo)
    } catch (err) {
        res.status(500).json({
            error: err.message
        })
    }
})
// 修改任务
app.patch('/todos/:id', (req, res) => {
    res.send(`patch /todos/${req.params.id}`)
})
// 删除任务
app.delete('/todos/:id', (req, res) => {
    res.send(`delete /todos/${req.params.id}`)
})

app.listen(3000, () => {
    console.log('server is running!!')
})

注意:需要先配置解析请求体的方式,然后,验证数据的时候lastTodo不要验证lastId,如果是undefined的话会报错,然后将数据保存到db中

savaDb的代码如下:

const fs = require('fs');
const { promisify } = require('util');
const path = require('path')

const readFile = promisify(fs.readFile)
const writeFile = promisify(fs.writeFile)
const dbPath = path.join(__dirname, './db.json')
exports.getDb = async () => {
    let data = await readFile(dbPath, 'utf8')
    return JSON.parse(data)
}

exports.saveDb = async (db) => {
    const data = JSON.stringify(db, null, '  ')
    await writeFile(dbPath, data);
}

注意:传入writeFile的数据需要是字符串格式,传入json格式会报错,JSON.stringify的第三个参数是确保json数据两个空格的缩进

7.修改数据和删除数据

const express = require('express');
const fs = require('fs');
const { getDb } = require('./db');
const { saveDb } = require('./db')
const app = express()

app.use(express.json())
app.use(express.urlencoded())

// 查询所有数据
app.get('/todos', async (req, res) => {
  try {
    let db = await getDb();
    res.status(200).json(db.todos)
  } catch (err) {
    res.status(500).json({
      error: err.message
    })
  }

})
// 查询单个数据
app.post('/todos/:id', async (req, res) => {
  try {
    const db = await getDb();
    const todo = db.todos.find(todo => todo.id.toString() === req.params.id);
    if (!todo) {
      return res.status(404).end()
    }
    res.status(200).json(todo)
  } catch (err) {
    res.status(500).json({
      error: res.message
    })
  }
})
// 添加数据
app.post('/todos', async (req, res) => {
  try {
    const todo = req.body;
    if (!todo) {
      return res.status(422).json({
        error: 'The name is required!!'
      })
    }
    const db = await getDb();
    const lastTodo = db.todos[db.todos.length - 1];
    const lastId = lastTodo ? lastTodo.id + 1 : 1
    todo.id = lastId;
    db.todos.push(todo)
    await saveDb(db)
    res.status(200).json(todo)
  } catch (err) {
    res.status(500).json({
      error: err.message
    })
  }
})
// 修改数据
app.patch('/todos/:id', async (req, res) => {
  try {
    const data = req.body;
    const db = await getDb();
    const todo = db.todos.find(todo => todo.id.toString() === req.params.id)
    todo.name = data.name;
    res.status(200).json(db.todos)
  } catch (err) {
    res.status(500).json({
      error: err.message
    })
  }
})
// 删除数据
app.delete('/todos/:id', async (req, res) => {
  try {
    const db = await getDb()
    const index = db.todos.findIndex(todo => todo.id.toString() === req.params.id);
    if (index == -1) { //判断如果没有查到index的情况
      return res.status(404).end()
    }
    db.todos.splice(index, 1)
    await saveDb(db)
    res.status(200).json(db)
  } catch (err) {
    res.status(500).json({
      error: err.message
    })
  }
})

app.listen(3000, () => {
  console.log('server is running!!!')
})