Skip to content

十、中间件的概念

1、Express中间件的格式

Express的中间件,本质上就是一个function处理函数

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

app.get('/',function(req,res,next)=>{
	next()
})
app.listen(3000)

注意:中间件函数的形参列表中,必须包含next参数,而路由处理函数中只包含req和res

2、next函数的作用

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

3、Expres中间件的初体验

1、格式:

js
const md=function(req,res,next){
	console.log("这是一个中间件")
	next()
}

2、全局生效的中间件

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

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

js
const md=function(req,res,next){
	console.log("这是一个中间件")
	next()
}
app.use(md)

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

js
//全局生效的中间件
app.use(function(req,res,next){
	console.log("这是一个最简单的中间件函数")
	next()
})

4、中间件的作用

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

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

// 注册全局中间件
app.use((req,res,next)=>{
    const time=Date.now()
    // 为req对象,挂载自定义属性,从而把时间共享给后面的所有路由
    req.startTime = time
    next()
})

app.get('/',(req,res)=>{
    res.send("homePage"+req.startTime)
})
app.post('/user',(req,res)=>{
    res.send("userPage"+req.startTime)
})


app.listen(80,()=>{
    console.log("服务器启动了");
})

5、定义多个全局中间件

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

js
app.use((req,res,next)=>{
   	console.log("调用第一个全局中间件")
    next()
})
app.use((req,res,next)=>{
   	console.log("调用第二个全局中间件")
    next()
})
app.get('/user',(req,res)=>{
    res.send("homePage")
})

6、局部生效的中间件

不适用app.use()定义的中间件,叫做局部生效的中间件

js
//定义中间件函数md
const md=(req,res,next)=>{
	console.log("这是中间件函数")
	next()
}
app.get('/',md,(req,res)=>{
	res.send("homepage")
})

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

7、定义多个局部中间件

js
const md1=(req,res,next)=>{
	console.log("这是第一个中间件函数")
	next()
}
const md2=(req,res,next)=>{
	console.log("这是第二个中间件函数")
	next()
}
app.get('/',md1,md2,(req,res)=>{res.send("homepage")})
//等价于
//app.get('/',[md1,md2],(req,res)=>{res.send("homepage")})

8、了解中间件的5个注意事项

  1. 一定要在路由之前注册中间件
  2. 客户端发送过来的请求,可以连续调用多个中间件进行处理
  3. 执行完中间件的业务的代码之后,不要忘记调用next()函数
  4. 为了防止代码逻辑混乱,调用next()函数后不要再写额外的代码
  5. 连续调用多个中间件时,多个中间件之间,共享req和res对象

4、中间件的分类

中间件分5大类

  1. 应用级别的中间件
  2. 路由级别的中间件
  3. 错误级别的中间件
  4. Express内置的中间件
  5. 第三方的中间件

1、引用级别的中间件

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

js
app.use((req,res,next)=>{
	next()
})
app.get('/',md,(req,res)=>{
	res.send('homepage')
})

2、路由级别的中间件

js
router.use((req,res,next)=>{
	next()
})

3、错误级别的中间件

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

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

js
app.use((err,req,res,next)=>{
	res.send(err.message)
})

注意:错误级别的中间件和其他中间件不一样,必须放在所有路由之后

4、Express内置的中间件

内置3个常用的中间件

  • express.static快速托管静态资源的内置中间件

  • express.json解析JSON格式的请求体数据(仅在4.16.0+版本中可用)

  • express.urlencoded解析URI-encoded格式的请求体数据(仅在4.16.0+版本中可用)

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

5、第三方中间件

5、自定义中间件

js
const express=require('express')

const app=express()

app.use((req,res,next)=>{
    let str=""
    req.on("data",(chunk)=>{
        str+=chunk
    })
    req.on("end",()=>{
        console.log(str);
    })
    next()
})

app.post('/user',(req,res)=>{
    res.send("post请求")
})


app.listen(8080,()=>{
    console.log("服务器运行了!");
})

1、使用querystring模块解析请求体数据

querystring模块的parse()函数,可以轻松把查询字符串解析为对象的格式

js
const qs=require('querystring')

const body=qs.parse(str)

2、将解析出来的数据对象挂载为req.body

上游的中间件和下游的中间件及路由之间,共享同一份req和res。因此,我们可以将解析出来的数据,挂载为req的自定义属性,命名为req.body,供下游使用

js
app.use((req, res, next) => {
    let str = ""
    req.on("data", (chunk) => {
        str += chunk
    })
    req.on("end", () => {
        const body = qs.parse(str)
        req.body = body
        next()
    })
})

app.post('/user', (req, res) => {
    console.log(req.body);
    res.send(req.body)
})

3、拆分中间件

js
//md1.js
const qs = require('querystring')

const md1=(req, res, next) => {
    let str = ""
    req.on("data", (chunk) => {
        str += chunk
    })
    req.on("end", () => {
        const body = qs.parse(str)
        req.body = body
        next()
    })
}

module.exports=md1
js
//test.js
const md1 = require('./拆分')

app.use(md1)