express的中间件

中间件(Middleware) 指的是业务流程的中间处理环节。

express中间件的调用流程

当一个请求到达express服务器之后,可以连续调用多个中间件来对这次请求进行预处理

image-20220601154213473

express中间件的格式

express的中间件,本质上就是一个function处理函数,express中间件的格式如下:

image-20220601154419506

next的作用

next函数是实现多个中间件连续调用的关键,它表示把流转关系转交给下个中间件或路由

image-20220601154726626

定义中间件函数

定义一个简单的中间件

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

// 常量mw指向的就是一个中间件函数
const mw = (req, res, next) => {
    console.log('这是一个简单的中间件函数')
    // 注意:在当前中间件的业务处理完毕后,必须调用next()函数
    next()    // 表示把流转关系转交给下一个中间件或路由
}

app.listen(80, () => {
    console.log('http://localhost')
})

全局生效的中间件

客户端发起的任何请求,在到达服务器后都会触发的中间件就叫做全局生效的中间件。

通过app.use(中间件函数)即可定义一个全局生效的中间件:

const mw = (req, res, next) => {
    console.log('这是一个简单的中间件函数')
    next()
}

app.use('mw')
const express = require('express')
const app = express()

const mw = (req, res, next) => {
    console.log('这是一个简单的中间件函数')
    next()
}

app.use('mw')    // 把mw注册为全局中间件

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

app.post('/', (req, res) => {
    res.send('got a post request')
})

app.listen(80, () => {
    console.log('http://localhost')
})

向服务器发起get,post请求后,就会在终端打印出'这是一个简单的中间件函数'。意味着客户端发起请求后,数据先经过了中间件的处理,再流转给下一个中间件或路由。

定义全局中间件的简化形式

// 定义全局中间件的简化形式
app.use((req, res, next) => {
    console.log('这是简单的中间件函数')
    next()
})

中间件的作用

在多个中间件之间,共享同一份req和res。基于这样的特性,我们可以在上游的中间件中,统一为req或res对象添加自定义的属性或方法,供下游的中间件或路由进行使用。

image-20220601161444424

示例:

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

const mw = (req, res, next) => {
    const time = Date.now() // 获取到请求到达服务器的时间
    req.startTime = time    // 为req对象挂载一个自定义属性,从而共享时间给后面的所有路由
    next()
}

app.use('mw')

app.get('/', (req, res) => {
    res.send(req.startTime)    //可以通过路由来访问到中间件的属性
})

app.post('/', (req, res) => {
    res.send(req.startTime)
})

app.listen(80, () => {
    console.log('http://localhost')
})

定义多个全局中间件

可以使用app.use()来连续定义多个全局中间件。客户端请求到达服务器后,会按照中间件定义的先后顺序依次进行调用。如下:

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

// 定义第一个中间件
app.use((req, res, next) => {
    console.log('我是第1个中间件')
    next()
})

// 定义第二个中间件
app.use((req, res, next) => {
    console.log('我是第2个中间件')
    next()
})

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

app.listen(80, () => {
    console.log('http://localhost')
})

局部生效的中间件

不使用app.use()定义的中间件就是局部生效的中间件,代码如下:

// 定义中间件函数 mw1
const mw1 = function (req, res, next) {
    console.log('这是中间函数')
    next()
}

// mw1 这个中间件只在“当前路由中生效”,这种用法属于“局部生效的中间件”
app.get('/', mw1, (req, res) => {
    res.send('home page')
})

// 中间件 mw1 不会影响下面这个路由
app.get('/user', (req, res) => { res.send('user page.') })

定义多个局部生效的中间件函数

可以在路由中,通过如下两种等价的方式,使用多个局部中间件:

// 以下两种写法一模一样,挑自己喜欢的用即可
app.get('/', mw1, mw2, (req, res) => { res.send('home page.') })
app.get('/', [mw1, mw2], (req, res) => { res.send('home page.') })

中间件的使用注意事项

  • 一定要在路由使用中间件之前定义中间件,即要在路由前注册中间件
  • 客户端发送过来的请求,可以连续调用多个中间件进行处理
  • 执行完中间件的业务代码后,要调用next()函数
  • 为了防止代码逻辑混乱,调用完next()之后不要再写其他代码
  • 连续调用多个中间件时,中间件之间共享req跟res对象

中间件的分类

Express官方把常见的中间件用法分成了五大类,分别是:

  • 应用级别的中间件
  • 路由级别的中间件
  • 错误级别的中间件
  • Express 内置的中间件
  • 第三方的中间件

应用级别的中间件

绑定到app实例上的中间件,叫做应用级别的中间件,如app.use(),app.get(),app.post()

示例:

// 应用级别中间件(全局中间件)
app.use((req, res, next) => {
    next()
})

// 应用级别中间件(局部中间件)
app.get('/', mw1, (req, res) => {
    res.send('home page')
})

路由级别的中间件

绑定到express.Router()实例上的中间件就叫做路由级别的中间件。

它的用法和应用级别的中间件没有任何区别。

只不过,应用级别的中间件时绑定到app实例上,路由级别的中间件绑定到router实例上

演示如下:

const app = express()
const router = express.Router()

router.use((req, res next) => {
    console.log('time:', Date.now())
    next()
})

app.use('/', router)

错误级别的中间件

错误级别的中间件是专门用来捕获整个项目中发生的异常错误,从而防止项目异常崩溃的问题。

错误级别中间件的function处理函数中必须有4个形参,形参顺序从前到后分别是(err, req, res, next)。

app.get('/', (req, res) => {    // 路由
    throw new Error('错误!')    // 模拟抛出一个错误
    res.send('home page')    // 上面抛出错误后,后续代码不会再运行
})

app.use((err, req, res, next) => {    // 错误级别的中间件
    console.log('发生了错误' + err.message)    // 在服务器打印错误信息
    res.send('error!' + err.message)    // 向客户端响应错误内容
})

示例:

没有捕获错误前,发起get请求,报错会直接造成程序崩溃

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

app.get('/', (req, res) => { // 路由
    throw new Error('错误!') // 模拟抛出一个错误
    res.send('home page') // 上面抛出错误后,后续代码不会再运行
})

app.listen(80, () => {
    console.log('http://localhost')
})

image-20220601174141055

捕获错误后:

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

app.get('/', (req, res) => { // 路由
    throw new Error('服务器内部发生错误!') // 模拟抛出一个错误
    res.send('home page') // 上面抛出错误后,后续代码不会再运行
})

app.use((err, req, res, next) => { // 错误级别的中间件
    console.log('发生了错误:' + err.message) // 在服务器打印错误信息
    res.send('error!' + err.message) // 向客户端响应错误内容
})

app.listen(80, () => {
    console.log('http://localhost')
})

image-20220601174415987

注意:错误级别的中间件与其他中间件不同,它需要注册在最后面!

Express内置的中间件

Express内置了3个常用的中间件 (截至至4.16.0)

  • express.static 快速托管静态资源的内置中间件
  • express.json 解析JSON格式的请求体数据(4.16.0+可用)
  • express.urlencoded 解析URL-encoded格式的请求体数据(4.16.0+可用)
// 配置解析 application/json 格式数据的内置中间件
app.use(express.json())
// 配置解析 application/x-www-form-urlencoded 格式数据的内置中间件
app.use(express.urlencoded({ extended: false }))
express.json()

如果前端发送了一个json格式的数据到服务器,则需要通过express.json()这个中间件来对数据进行处理

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

// 配置express.json中间件
app.use(express.json())    // 挂载后,req.body能正常返回内容,而不是undefined

app.post('/user', (req, res) => {
    // 在服务器,可以使用req.body这个属性来接收客户端发送过来的请求体数据
    // 默认情况下,如果不配置解析表单数据的中间件,则req.body默认等于undefined
    console.log(req.body)
    res.send('ok')
})

app.listen(80, () => {
    console.log('http://localhost')
})

配置RestClient测试文件

@url = http://localhost

@json=Content-Type: application/json

post {{url}}
{{json}}

// post一个json格式的数据
{
    "name":"sb",
    "age":1
}

成功后,会在终端返回这个json

image-20220601180351845

express.urlencoded()

如果前端以form表单的形式发送数据到服务器,则需要通过express.urlencoded()这个中间件来对数据进行处理

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

// 配置express.json中间件
app.use(express.urlencoded({ extended: false }))    // 挂载后,req.body能正常返回内容,而不是undefined

app.post('/', (req, res) => {
    // 在服务端,可以通过req.body来获取JSON格式的表单数据和url-encoded格式的数据
    console.log(req.body)
    res.send('ok')
})

app.listen(80, () => {
    console.log('http://localhost')
})

配置RestClient测试文件

@url = http://localhost

@form = Content-Type: application/x-www-form-urlencoded

post {{url}}
{{form}}
username=tom&pwd=123

成功后会返回一个对象

image-20220601181632249

第三方的中间件

类似python的库,是非express内置的,由第三方开发出来的中间件。

例如,在[email protected]之前的版本,经常使用到body-parser这个第三方中间件来解析请求体数据

使用方法如下:

  1. 运行npm install body-parser安装中间件
  2. 使用require导入
  3. 使用app.use()来注册中间件
最后修改:2022 年 08 月 25 日
如果觉得我的文章对你有用,能不能v我50参加疯狂星期四