Express框架的简单入门(4.x)

Express中文官网:https://www.expressjs.com.cn/

英文API手册:http://expressjs.com/en/4x/api.html

一、安装并初识Express

1、初始化一个npm项目:

npm init -y

2、安装express:

npm install express
# 指定版本:
npm install express@4.18.2

3、编写一个入门案例app.js:

const express = require('express');

const app = express();
const port = 3000;

app.get("/", (req, res) => {
    res.send("Hello World!");
})

app.get("/user", (req, res) => {
    res.send("这是/user路径的返回")
})

app.listen(port, () => {
    console.log(`Server running at http://localhost:${port}/`);
})

4、运行app.js:

node app.js

# 动态运行app.js,当有文件变动时,能够立即生效
nodemon app.js

5、此时访问 http://localhost:3000

1686727429991230.png

如此简单,一个后端Server服务就启动起来了!


二、一个简单的TODO案例,练习路由

1、简单路由设计:

const express = require('express');

const app = express();
const port = 3000;

app.get("/todos", (req, res) => {
    res.send("get /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(port, () => {
    console.log(`Server running at http://localhost:${port}/`);
})

2、现在没有DB数据库,我们就先把数据存在本地的一个json文件中db.json:

{
    "todos": [
        {
            "id" : 1,
            "title" : "吃饭"
        },
        {
            "id" : 2,
            "title" : "睡觉"
        },
        {
            "id" : 3,
            "title" : "写代码"
        }
    ]
}

3、在app.js中使用fs模块,进行db.json文件的操作,封装一个工具类db.js:

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

//将callBack形式的异步API,转化为promise形式, promise形式可以防止回调地狱
const readFile = promisify(fs.readFile) 
const writeFile = promisify(fs.writeFile)

const dbPath = path.join(__dirname, './db.json')

exports.getDb = async () => {
    const data = await readFile(dbPath, 'utf8')
    return JSON.parse(data)
} 

exports.saveDb = async db => {
    // 最后的两个参数可以让写入 db.json 的数据可读性更好
    const data = JSON.stringify(db, null, '  ') 
    await writeFile(dbPath, data)
}

4、整个增删改查逻辑代码如下app.js:

const express = require('express')
const fs = require('fs')
const { getDb, saveDb } = require('./db.js')

const app = express()
// /配置允许解析表单请求体application/json
app.use(express.json())
// 配置允许解析表单请求体application/x-www-form-urlencoded
app.use(express.urlencoded())

const port = 3000

app.get("/todos", async (req, res) => {
    try {
        const db = await getDb()
        return res.status(200).json(db.todos)
    } catch ( err ) {
        return res.status(500).json({
            error: err.message
        })
    }
})

app.get("/todos/:id", async (req, res) => {
    try {
        const db = await getDb()
        const todo = db.todos.find(todo => todo.id === Number.parseInt(req.params.id))
        if (!todo) {
            return res.status(404).end()
        }
        return res.status(200).json(todo)
    } catch (err) {
        return res.status(500).json({
            error: err.message
        })
    }
})

app.post("/todos", async (req, res) => {
    try {
        // 1.获取请求体数据
        const todo = req.body

        // 2.验证数据
        if (!todo.title) {
            return res,status(422).json({
                error : 'The field title is required'
            })
        }

        // 3.数据验证通过,保存数据
        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)
        return res.status(200).json(todo)
    } catch (err) {
        return res.status(500).json({
            error: err.message
        })
    }
})

app.patch("/todos/:id", async (req, res) => {
    try {
        // 1.获取请求体数据
        const todo = req.body

        // 2.查找到对应的数据
        const db = await getDb();
        const oldTodo = db.todos.find(todo => todo.id === Number.parseInt(req.params.id))

        if (!oldTodo) {
            return res.status(404).end();
        }

        // 3.合并todo,保存数据
        Object.assign(oldTodo, todo)
        await saveDb(db)
        return res.status(200).json(oldTodo)
    } catch (err) {
        return res.status(500).json({
            error: err.message
        })
    }
})

app.delete("/todos/:id", async (req, res) => {
    try {
        // 1.获取请求体数据
        const todo = req.body

        // 2.查找到对应的数据的索引
        const db = await getDb();
        const index = db.todos.findIndex(todo => todo.id === Number.parseInt(req.params.id))

        if (index === -1) {
            return res.status(404).end()
        }

        // 3.删除对应索引的数据
        db.todos.splice(index, 1)
        await saveDb(db)
        return res.status(204).end()
    } catch (err) {
        return res.status(500).json({
            error: err.message
        })
    }
})

app.listen(port, () => {
    console.log(`Server running at http://localhost:${port}/`)
})


三、Express中的中间件

1686823804447345.png

1、简单案例:对所有的请求进行访问日志打印(中间件的引入):

const express = require('express')
const fs = require('fs')
const { getDb, saveDb } = require('./db.js')

const app = express()
// /配置允许解析表单请求体application/json
app.use(express.json())
// 配置允许解析表单请求体application/x-www-form-urlencoded
app.use(express.urlencoded())

// 编写我们自己的中间件,实现日志功能
app.use((req, res, next) => {
    console.log(req.method, req.url, Date.now())
    next()
})

const port = 3000

app.get("/", async (req, res) => {
    res.send('/get /')
})

app.get("/user", async (req, res) => {
    res.send('/get user')
})

app.post("/save", async (req, res) => {
    res.send('/post save')
})

app.put("/update", async (req, res) => {
    res.send('/put update')
})

app.delete("/remove", async (req, res) => {
    res.send('/delete remove')
})

app.listen(port, () => {
    console.log(`Server running at http://localhost:${port}/`)
})

日志显示结果如下:

[nodemon] starting `node app.js`

Server running at http://localhost:3000/
GET /user 1686823299996
POST /save 1686823307997
PUT /update 1686823314216
DELETE /remove 1686823319492

经过上面的实现,我们发现Nodejs中的中间件,与JAVA中的AOP思想很像;

如果有多个中间件,那么就会按照顺序,自上而下地依次执行,中间件中一定要记得通过next()将请求传递给下一个中间件

中间件use的位置非常重要,如果在某个路由之后使用中间件,那么这个路由对应的请求就不会进入这个中间件!

2、Express中的中间件函数:

在Express中,中间件就是一个可以访问请求对象、响应对象和调用next方法的一个函数;

1686824126670205.png

我们可以看到,在Router路由中,也可以使用中间件函数,将请求处理完成后,交给后续的中间件/路由进行处理;

【注意】如果当前的中间件功能没有结束请求-响应周期,则必须调用next()将控制全传递给下一个中间件功能。否则,该请求将被挂起!

3、Express中中间件的分类:

  • 应用程序级别中间件:

  • 路由级别中间件:

  • 错误处理中间件:

  • 内置中间件

  • 第三方中间件

Express官方罗列的常见的第三方中间件列表:https://www.expressjs.com.cn/resources/middleware.html

jiguiquan@163.com

文章作者信息...

留下你的评论

*评论支持代码高亮<pre class="prettyprint linenums">代码</pre>

相关推荐