express的中间件
中间件(Middleware) 指的是业务流程的中间处理环节。
express中间件的调用流程
当一个请求到达express服务器之后,可以连续调用多个中间件来对这次请求进行预处理
express中间件的格式
express的中间件,本质上就是一个function处理函数
,express中间件的格式如下:
next的作用
next函数是实现多个中间件连续调用的关键,它表示把流转关系转交给下个中间件或路由
定义中间件函数
定义一个简单的中间件
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对象添加自定义的属性或方法,供下游的中间件或路由进行使用。
示例:
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')
})
捕获错误后:
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')
})
注意:错误级别的中间件与其他中间件不同,它需要注册在最后面!
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
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
成功后会返回一个对象
第三方的中间件
类似python的库,是非express内置的,由第三方开发出来的中间件。
例如,在[email protected]之前的版本,经常使用到body-parser这个第三方中间件来解析请求体数据
使用方法如下:
- 运行
npm install body-parser
安装中间件 - 使用require导入
- 使用app.use()来注册中间件