这章内容内容包括:
- Koa.js 框架快速入门
- Koa 极简实现
- 常用 Koa 中间件
- 认识 Restful Api
- 实现简单的 Restful API
# Koa.js 框架快速入门
Koa 是一个 web 框架,由 Express 幕后的原班人马打造, 致力于成为 web 应用和 API 开发领域中的一个更小、更富有表现力、更健壮的基石。 通过利用 async/await 函数,摆脱回调地狱,并有力地增强错误处理。Koa 并没有捆绑任何中间件,而是提供了一套优雅的中间件方法,让你自由的按需加载,这种作法褒贬不一,各大厂都会根据团队需求来封装自己 node 框架,衍生出的比较有名的有 Egg.js、nest.js、thinkjs 和 hapi,有兴趣的可以自行搜索相关资料。
Koa 应用程序是一个包含一组中间件函数的对象,它是按照类似堆栈的方式组织和执行的。 Koa 类似于其他中间件系统,然而,一个关键的设计点是在其低级中间件层中提供高级语法糖。 尽管提供了相当多的有用的方法 Koa 仍保持了一个很小的体积,因为没有捆绑中间件。
还是那个 Hello Uniapp 应用:
const Koa = require('koa');
const app = new Koa();
app.use(async ctx => {
ctx.body = 'Hello Uniapp';
});
app.listen(3000);
Koa 中间件以更传统的方式级联,使用 async 功能,我们可以实现 “真实” 的中间件。
Koa 中间件包含两个参数 ctx 和 next。参数 next 是一个函数,它的作用是将处理控制权交给下一中间件。Koa 中间件采用级联代码方式执行。其中间件参数 next 的级联执行逻辑如图所示。
Koa 中间件执行顺序原理图

执行流程,事件从最外层逐层触发,每层都会一进一出穿过两次,且最先穿入的一层最后穿出,到达最后一层最后冒泡返回。好比水分进出洋葱一样,洋葱的每层相当于中间件,水分输入相当于请求,水分输出相当于相应。
下面以欢迎订阅,Uniapp从入门到进阶的响应作为示例,请求开始进入中间件1,打印欢迎订阅,,当遇到第一个 next() 则该函数暂停并将控制传递给定义的下一个中间件2,打印Uniapp,遇到第二个 next() 发现没有更多的中间件执行了,于是返回打印从入门,再向上打印到进阶,然后跳出中间往下执行其他代码。
const Koa = require('koa');
const app = new Koa();
// 中间件 1
app.use(async (ctx, next) => {
console.log('欢迎订阅,')
await next();
console.log('到进阶')
});
// 中间件 2
app.use(async (ctx, next) => {
ctx.body = 'Uniapp';
await next();
console.log('从入门')
});
app.listen(3000, () => {
console.log('server is running at http://localhost:3000')
});
# Koa 极简实现
有的朋友可能之前已经了解过 Koa 的组成,网上也有很多的源码分析,我打算来简单的实现一个 mini 版本。麻雀虽小,但是核心依然是入口 Application 和 上下文 Context。
各模块的核心:
- Application:
use(),listen(),callback(),compose() - Context:整合
req,res到ctx对象中
接下来实现入口 Application.js:
const http = require('http') // 利用 http 模块
const Context = require('./Context') // 导入 Context 模块
module.exports = class Application {
constructor() {
this.middlewares = [] // 保存所有的中间件函数
}
// 构建ctx,传入到中间件集合,执行next递归
callback() {
return async(req, res) => {
// 初始化ctx
const ctx = new Context(req, res);
//调用 compose 函数,依次处理所有中间件函数
const fn = this.compose(this.middlewares);
await fn(ctx)
// 最后返回res body
this.responseBody(ctx)
}
}
// 简单粗暴处理res body
responseBody(ctx) {
const content = ctx.body;
ctx.res.end(content);
console.log(content)
// 可加入类型判断,error错误处理流程
// ...
}
// 核心:递归中间件,即所谓的 `next()` 方法,先执行第一个
compose(middlewares) {
return ctx => {
const useMiddleware = i => {
let fn = middlewares[i] //遍历中间件集合
if (!fn) {
return
}
return fn(ctx, () => useMiddleware(i + 1)) //递归执行中间件方法,并且传到一下层
}
return useMiddleware(0)
}
}
// 挂载中间件
use(middleware) {
//打包中间件集合,middleware实则是个方法
this.middlewares.push(middleware)
}
// 启动服务器
listen(...args) {
const server = http.createServer(this.callback())
server.listen(...args)
}
}
