0%

Koa 入门

本文主要介绍 Koa 简单使用,适用于 Koa 刚需初次使用者。为了更好地使用 Koa,文章内 demo 会涉及 mongoDB 等一些数据库基础知识。总的来说,Koa 学习成本还是比较低的。

什么是 Koa ?

Koa 是一个基于 Node.js 的 web 框架,由 Express 团队开发,但与 Express 相比较,Koa 显得尤为轻量,与其理念 “Koa 致力于成为 web 开发中的基石” 相呼应,egg.js 就是基于 koa 开发的。

Express 和 Koa 简单写法

首先 hello world 走起~~

1
2
3
4
5
6
7
8
// Express 写法
const express = require('express')
const app = express()

app.get('/', function (req, res) {
res.send('Hello World!')
});
app.listen(3000);
1
2
3
4
5
6
7
8
9
10
11
// Koa 写法
const Koa = require('koa');
const KoaRouter = require('koa-route');

conast app = new Koa(); // 2.0 版本采用构造函数创建 app 实例
const route = new KoaRouter();

app.use(route.get('/', function *(){
this.body = 'Hello World';
}));
app.listen(3000);

对比 hello world 简单例子之后,不难发现为什么 koa 框架轻量而敏捷的原因了,koa 主要是对 node 的 HTTP 模块进行了封装,不在内核方法中绑定任何中间件,可通过用户自行 require 中间件去扩展。

这里插一句题外话,node.js 不支持 es6 import 语法,还在实验阶段。 目前还是采用 commonjs require 写法,若坚持使用 es6 的 module 功能,可以通过 babel 解决(个人觉得服务端应用程序没多大必要使用 babel)

Koa 几个核心概念

Koa 的中间件机制

洋葱模型图(在当前中间件中位于 next() 之后的代码会暂停执行,直到最后一个中间件执行完毕,再自下而上依次执行每个中间件中 next 后的代码,类似于栈的先进后出。)

它实现的核心就是借助 compose 这个库来实现的
每收到一个 http 请求,koa 就会调用通过 app.use() 注册的 async 函数,并传入 ctx 和 next 参数。koa 把很多 async 函数组成一个处理链,每个 async 函数都可以做一些自己的事情,然后用 await next() 来调用下一个 async 函数,每个 async 函数就称为中间件。

中间件的几个特点:

  • 存储:以数组形式存储中间件。
  • 状态管理:所有的状态变更,都交给 ctx 对象,无需跨中间件传递参数。
  • 流程控制:以递归的方式进行中间件的执行,将下一个中间件的执行权交给正在执行的中间件,即洋葱圈模型。
  • 异步方案:用 Promise 包裹中间件的返回结果,以支持在上一个中间件内部实现 await 逻辑。

每个中间件函数所传入的 next 变量,都是对“下一个中间件执行行为”的封装(这里简单理解为控制权)

1
2
3
4
app.use(async (ctx, next) => {
console.log('test')
await next()
})

上面的匿名函数就可以理解为一个中间件。

compose 伪代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function compose (middleware) {
return function (context, next) {
let index = -1 // index用于标识上次执行到了哪个中间件(-1表示第0个)
// 执行第一个中间件函数
return dispatch(0)
function dispatch (i) { // i用于标识即将执行哪个中间件(0表示第1个)
if (i <= index) return Promise.reject(new Error('next() called multiple times'))
index = i
let fn = middleware[i]
if (i === middleware.length) fn = next
if (!fn) return Promise.resolve()
try {
return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
} catch (err) {
return Promise.reject(err)
}
}
}
}

注意点:

  1. 一个中间件内不要调用多次 next
  2. 定义异步中间件

错误捕获和异常信息

  1. 捕获中间件内部错误(注册一个中间件即可,其底层也是通过继承 node.js events 对象实现)
1
2
3
4
5
6
7
app.use(async (ctx, next) => {
try {
await next()
} catch (error) {
ctx.app.emit('error', error, ctx)
}
})
  1. 通过 app 来派发错误,监听 app 上的 error 事件做进一步的统一处理和集中管理
1
2
3
4
app.on('error', (err, ctx) =>{
<!--将错误信息存入数据库等-->
console.error('server error', err)
});
  1. 自定义异常抛出 (虽然 koa 中内置了错误处理机制,但是实际业务开发中,我们希望能够自定义错误处理方式,这里我们基于自定义 HttpException 基类扩展想要的异常信息)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 自定义异常抛出
*/
app.use(async (ctx, next) => {
try {
await next()
} catch (error) {
<!--这里自定义错误处理方式-->
if (error instanceof HttpException) {
ctx.body = {
message: error.msg,
code: error.code,
data: error.data
}
}
}
})

content、request、response 对象

服务端开发的重点是对 HTTP Request 和 HTTP Response 两个对象的封装和处理,content 上下文环境对象,Koa 将这些对象统一集成到 ctx 中。

属性 含义
context
req
res
request
response

注意:GET 参数都可以直接通过 Request 对象获取,POST 参数需要引入中间件 先 parse,才能取值

每个 http 请求的数据都在各自的 ctx 中维护
这块建议可以去看看源码(目前还没看太懂~~)

Koa 生态常用的中间件

  • koa2-cors:解决跨域问题
  • koa-bodyparser:解析 post 请求传递的参数
  • koa-session:让无状态的http拥有状态,基于cookie实现的后台保存信息的session
  • koa-router:后台会接受到各种请求的url,路由会根据不同的url来使用不同的处理逻辑。
  • koa-static:请求img、js、css等文件时,不需要其他逻辑,只需要读取文件

最后

笔者基于 react-koa-mongodb,做了一个非常简单的登录注册功能,通过 demo 演示去更好地理解和使用 koa 等知识点。

1
2
3
4
5
6
7
8
9
10
demo 一些前置条件
mongodb
+ 安装 mongodb
+ 启动服务设置数据库目录:
sudo ./mongod --dbpath=/Users/zhangtengjin/data/db
+ 连接数据库
sudo ./mongo (进入客户端命令行模式,默认连接test db)

momngodb 可视化 gui 工具
Navicat Premium / Robo 3T GUI

确定 node、mongoDB、navicat 等工具已安装基础下启动进行

附 demo 地址: https://github.com/zhangtengjin/react-koa-mongodb-demo

参考资料:

https://koajs.com/
https://nodejs.org/docs/latest-v13.x/api/
https://mongoosejs.com/docs/guide.html

-------------本文结束, 感谢您的阅读-------------