概念
Express的本质就是一个基于路由和中间件的Web框架,Express的本质就是一系列中间件函数的调用
在Express中 中间件是一个接收 请求体 和 响应头的 回调函数
简单来说,中间件就是放在请求和响应之间的一个回调函数,用来处理请求的中间步骤
中间件的匹配规则是:
- Express 会找到第一个匹配的中间件并执行。
- 如果中间件直接返回了结果,那么请求处理就结束了。
- 如果没有返回结果,可以通过
next()传递控制权到下一个中间件,继续处理请求并返回结果。
通用中间件
import express from 'express'
const app = express()
// 第一个参数是request对象,第二个参数是response对象, 第三个参数是next函数
// + request对象: 请求体对象 => 基于 http 模块的 request 对象扩展而来
// + response对象: 响应体对象 => 基于 http 模块的 response 对象扩展而来
// + next函数: 用于调用下一个中间件
app.use((req, res, next) => {
// 在中间件回调中可以执行任何操作
// 满足条件的中间件会被依次回调。在这个过程中,请求体和响应头都是同一个
// 因此此时 对应的请求或响应并没有结束,还是同一个
req.name = '张三'
// 调用下一个中间件
next()
})
app.get('/', (req, res) => {
// 中间件处理完成后要么传递给下一个中间件,要么结束响应。
// 否则客户端会一直处于挂起状态,即一直处于加载中,直到超时
res.send(`hello ${req.name}`)
})
app.listen(3000, () => {
console.log('Server is running on port 3000')
})
路径中间件
import express from 'express'
const app = express()
// 通过 app.use 注册 通用中间件
// 通用中间件: 可以匹配所有请求路径和请求方法的中间件
app.use((req, res, next) => {
console.log('通用中间件')
// 通过 next 调用下一个 满足条件的 中间件 => next 方法是无参的
// 若不调用next(),则后续的中间件将不会被执行
next()
})
// 只要路径匹配,就会执行这个中间件 「 无论使用的是那种请求方法 」
app.use('/', (req, res, next) => {
console.log('路径匹配中间件')
next()
})
// 通过 app.get、app.post、app.put、app.delete等方法 注册 路由中间件
// 路由中间件: 只能匹配特定请求路径和请求方法的中间件
app.get('/', (req, res) => {
console.log('路由中间件')
res.send('hello world')
})
app.listen(3000, () => {
console.log('Server is running on port 3000')
})
路由中间件
import express from 'express'
const app = express()
app.get('/', (req, res) => {
// 获取请求路径
console.log('url', req.url) // => /?name=Klaus&age=23
// 获取请求路径中的路径部分
console.log('path', req.path) // => /
// 获取请求方法
console.log('method', req.method) // => GET
res.send('hello world')
})
app.listen(3000, () => {
console.log('Server is running on port 3000')
})
一次注册多个中间件
这样做的好处是,你可以将复杂操作拆分成多个中间件,依次执行。「 单一职责原则 」
如果每个中间件里调用next(),后面的中间件会依次执行。
如果没有调用next(),后面的中间件就不会被执行。
这是将复杂操作拆分成多个中间件的一种常见方法,特别适用于需要多步验证和数据处理的情况。
import express from 'express'
const app = express()
// 每种类型中间件都可以一次注册多个中间件
// 这些中间件会整合为数组并被依次回调
app.get('/', (req, res, next) => {
console.log('中间件1')
next()
}, (req, res, next) => {
console.log('中间件2')
next()
}, (req, res, next) => {
console.log('中间件3')
res.send('hello world')
})
app.listen(3000, () => {
console.log('Server is running on port 3000')
})
链式调用
import express from 'express'
const app = express()
// 每个中间件返回的都是 应用实例
// 因此可以链式调用
app
.use((req, res, next) => {
console.log('通用中间件')
next()
})
.use('/', (req, res, next) => {
console.log('路径匹配中间件')
next()
})
.get('/', (req, res) => {
console.log('路由中间件')
res.send('hello world')
})
app.listen(3000, () => {
console.log('Server is running on port 3000')
})
兜底页面
可以将一个 通用中间件 放在所有路由的最后边,从而进行路由兜底
app.use((req, res, next) => {
// 通过 status 方法 设置 响应码
res.status(404).json({ error: 'Not Found' });
});