Koa 中间件是一个带有 context 和 next 参数的函数,该函数支持 async/await 语法,在中间件中调用 next 函数将代码的执行权交给下一个中间件,中间件的工作被 next() 分为前后两部分,next() 返回一个 Promise 对象。Koa 官方用洋葱模型来解释中间件的调用顺序,如下图所示:

上图的意思是第一个中间件 next() 前面的代码最先执行,next() 后面的代码最后执行。第一个中间件调用 next() 之后第二个中间件开始工作,第二个中间件调用 next() 之后第三个中间件开始工作,第三个中间件是最后一个中间件,它不必调用 next()。当第三个中间件执行完时,第二个中间件 next() 后面的代码开始执行。比如在 Koa 应用中使用了如下的中间件:
ts
app.use(async (context, next) => {
try {
await next()
} catch (error: any) {
context.body = `出错了,${error}`;
}
})
app.use(async(context, next) => {
context.set('X-Response-Time', `${new Date().getTime()}`);
context.set({
'Access-Control-Allow-Origin':'*',
'Access-Control-Allow-Methods': 'GET,POST'
})
await next()
console.log('finish')
})
app.use(async (context) => {
console.log('set body')
context.body = 'Hello Koa';
})
第一个中间件用于处理错误,它能处理后面的中间件抛出的任何错误,第二个中间件console.log('finish') 会比第三个中间件 console.log('set body') 后执行。
Koa社区拥有众多的中间件,读者可访问 github.com/koajs/koa/w... 查看
@koa/router
@koa/router是Koa的路由中间件,它支持带参数的动态路由、嵌套路由,还支持在路由上使用多个中间件。它的基本用法如下:
ts
const app = new Koa()
const router = new Router()
router
.get('/', (context) => {
context.body = '我捕获根路径的get请求'
})
.get('/user', context => {
context.body = '我捕获路径是/user的get请求'
})
.post('/user', context => {
context.body = '我捕获路径是/user的post请求'
})
app
.use(router.routes())
.use(router.allowedMethods())
动态路由
动态路由指的是在路径上携带参数,@koa/router 使用 path-to-regexp 将路径字符串转成正则表达式。动态路径的代码如下:
ts
router
.get('/user/:id', (context) => {
context.body = `我捕获路径是/user/xx形式的的get请求,路径上的参数是${context.params.id}`
})
.get('/user/:id/:local', (context) => {
context.body = `我捕获路径是/user/xx/yy形式的的get请求,路径上的参数是${context.params.id}和${context.params.local}`
})
嵌套路由
嵌套路由是指在路由中使用路由,前面的示例代码中所有的路由都是平级的,嵌套路由让路由有父子关系,代码如下:
ts
const router = new Router()
const studentRouter = new Router()
studentRouter
.get('/:id', (context) => {
context.body = `我捕获路径为 /student/xx 形式的 get 请求`
})
router.use('/student', studentRouter.routes(), studentRouter.allowedMethods())
app
.use(router.routes())
.use(router.allowedMethods())
上述代码中 studentRouter 是 router 的子路由,studentRouter 的路径上不必添加 /student 前缀。
在路由上使用多个中间件
使用多个中间件处理匹配到的路径,代码如下:
ts
router
.get('/user/:id',
async (context, next) => {
console.log(context.params.id)
await next()
},
(context) => {
context.body = `我捕获路径为 /user/xx 形式的 get 请求`
})
上述代码使用两个中间件去处理 /user/:id,这两个中间件有不同的职责。
koa-compress
koa-compress 是一个用于数据压缩的中间件,使用它能够压缩数据提高传输速度,在 @koa/router 的基础上使用 koa-compress 代码如下:
ts
import compress from 'koa-compress'
app
.use(compress({
// content_type 是 MIME 类型
filter(content_type) {
// 当响应的 Content-Type 中包含 text 时才压缩数据
return /text/i.test(content_type)
},
// 当数据大小超过 500 bytes时压缩
threshold: 500,
}))
.use(router.routes())
.use(router.allowedMethods())
compress 除了可以配置上述代码中的 filter 和 threshold 字段,还有一些其他的配置项,访问github.com/koajs/compr... 查看更多的配置项。
koa-bodyparser
koa-bodyparser 基于 co-body 解析请求体中的数据,比如获取 POST 请求的参数,它支持 json、form、text 和 xml 类型的请求体,不支持 multipart/form-data,解析出的结果以对象的形式保存在 context.request.body 中。用法如下:
ts
import bodyParser from 'koa-bodyparser'
router
.post('/user', context => {
context.body = context.request.body
})
// 将 bodyParser 中间件放在第一个位置
app
.use(bodyParser())
.use(router.routes())
.use(router.allowedMethods())
bodyParser 有多个配置项,访问github.com/koajs/bodyp... 查看配置项详情。
koa-static
koa-static 是一个专门用来响应静态资源的中间件,客户端可能要加载很多静态资源,比如图片、CSS文件、JavaScript 文件等,单独为这些资源配置路由过于繁琐,使用 koa-static 只需要配置一个静态资源的存放目录,就能统一处理静态资源,代码如下:
ts
import koaStatic from 'koa-static'
// 这表明静态资源存放在 Node.js 当前的工作目录,
// 访问 http://localhost:3001/package.json 将得到项目根目录的 package.json 文件
// 访问 http://localhost:3001/static/index.css 将得到项目根目录的 /static/index.css文件
app.use(koaStatic(process.cwd()))
// 这表明静态资源存放在Node.js 当前工作目录下的 static 目录
// 访问 http://localhost:3001/index.css 将得到项目根目录的 /static/index.css文件
app.use(koaStatic(process.cwd() + '/static'))
koaStatic 接受两个参数,第一个参数是 koa-static 服务的目录,这个目录之外的静态资源不被 koa-static 服务,第二个参数可选,访问github.com/koajs/stati... 查看第二个参数的详情。
@koa/multer
@koa/multer 是一个基于 multer 实现文件上传的中间件,处理 context-type 为multipart/form-data 的请求,能实现单文件上传和多文件上传,它将 FormData 的文本信息保存在 context.request.body 中,将文件信息保存到 context.request.file 或者 context.request.files 中,下面是一个处理单文件上传的示例:
ts
import multer from '@koa/multer'
const upload = multer({
dest: '/uploads' // 指定文件的保存路径
});
// 这个路由用于单文件上传
router
.post(
'/upload',
upload.single('file'), // upload 中间件先处理,处理完再进入下一个中间件
async (context, next) => {
console.log('文件名:' + context.request.file.filename)
console.log('文件路径:' + context.request.file.path)
await next()
}
)
上述代码实现了单文件上传,此时文件信息保存在context.request.file中,如果是多文件上传,那么文件信息保存在context.request.files中,访问 github.com/koajs/multe... 了解 @koa/multer 更多详情。