中间件
中间件是 Koa 框架的核心概念之一,也是许多现代 Web 框架中广泛使用的设计模式。中间件的主要作用是对请求和响应进行预处理、后处理或拦截操作。
通过中间件机制,Koa提供了一个强大且灵活的方式来处理 HTTP 请求,使得开发者可以轻松地扩展和定制应用程序的行为。
基本概念
中间件是在请求和响应中间的处理程序,是一个功能独立的函数,它可以访问请求对象(request)、响应对象(response)和应用程序上下文(context)。
在 Koa 中,中间件是通过 app.use()
方法来使用的。每个中间件都是一个函数,通常是异步的,并且接受两个参数:
ctx
:上下文对象,是context的简写,代表 http 请求的上下文next
:用于调用下一个中间件的函数
中间件的执行顺序
Koa的中间件按顺序执行,类似于一个栈结构。每个中间件可以选择在调用 await next()
之前或之后执行的代码。
通过调用 await next()
,中间件将控制权交给下一个中间件,待其执行完毕后再返回当前中间件继续执行。
中间件的作用
- 日志记录:记录每个请求的详细信息,如请求路径、方法、响应时间等。
- 错误处理:捕获和处理请求过程中可能出现的错误,避免未处理的异常导致应用崩溃。
- 请求解析:解析请求体中的数据,如 JSON、表单数据等。
- 认证和授权:验证用户身份和权限,确保只有授权用户可以访问特定资源
- 响应修改:在响应发送给客户端之前对其进行修改,如添加响应头、设置响应状态码等。
基本使用
const Koa = require("koa")
const app = new Koa()
app.use(async (ctx, next) => {
console.log("我来组成头部")
next()
})
app.use(async (ctx, next) => {
console.log("我来组成身体")
next()
})
app.use(async (ctx, next) => {
console.log("我来组成尾部")
next()
})
app.use(async (ctx) => {
console.log("组装完成")
ctx.body = "Hello, Koa!"
})
app.listen(3000, () => {
console.log("Server running on http://localhost:3000")
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
链式调用
app.use()
返回 this
,因此可以链式调用。
app.use(middleware1)
app.use(middleware2)
app.listen(3000)
2
3
它等同于
app.use(middleware1)
.use(middleware2)
.listen(3000)
2
3
洋葱圈模型
在 Koa 中,中间件的执行顺序通常比喻为「洋葱圈」模型。这种模型形象的描述了中间件的执行顺序和流程。
先看一个 demo:
const Koa = require("koa")
const app = new Koa()
// 中间件1
app.use(async (ctx, next) => {
console.log("1")
next()
console.log("2")
})
// 中间件2
app.use(async (ctx, next) => {
console.log("3")
next()
console.log("4")
})
app.listen(3000, () => {
console.log("Server running on http://localhost:3000")
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
输出的结果是
1
3
4
5
2
3
4
在 Koa 中,中间件被 next()
方法分成了两部分。next()
方法上面部分的代码先执行,下面部分的代码会在后续中间件执行全部结束之后再执行,可以通过下图直观看出:
在洋葱模型中,每一层相当于一个中间件,用来处理特定的功能,比如错误处理、认证授权等等。其处理顺序先是 next()前的请求
(request,从外层到内层)然后执行 next()
函数,最后是 next()后的响应
(response,从内层到外层),也就是说每一个中间件都有两次处理时机。
异步处理
如果中间件中存在一些异步的代码,Koa也提供了统一的处理方式。
async / await
- async 声明一个异步函数
- await 后面跟一个 Promise 对象
如果要使用 await,需要在函数声明前加上 async