Toa
March 24, 2020 · View on GitHub
简洁而强大的 web 框架。
Thanks to Koa and it's authors
Summary
Toa 简介
Toa 修改自 Koa,基本架构原理与 Koa 相似,context、request、response 三大基础对象几乎一样。
Toa 是基于 thunks 组合业务逻辑,来实现异步流程控制和异常处理。thunks 是一个比 co 更强大的异步流程控制工具,支持所有形式的异步控制,包括 callback,promise,generator,async/await 等。
Toa 支持 Node.js v0.12 以上,但在高版本中将有更好的体验和性能,如 >=v4 的版本支持 generator 函数,>=v7 的版本支持 async/await 函数,这些特性支持用同步逻辑编写非阻塞的异步程序。
Toa 与 Koa 学习成本和编程体验是一致的,两者之间几乎是无缝切换。但 Toa 去掉了 Koa 的 级联(Cascading) 逻辑,强化中间件,尽量削弱第三方组件访问应用的能力,使得编写大型应用的结构逻辑更简洁明了,也更安全。
安装 Toa
npm install toa
Application
一个 Toa Application(以下简称 app)由一系列 中间件 组成。中间件 是指通过 app.use 加载的同步函数、thunk 函数、generator 函数或 async/await 函数。
对于 web server 的一次访问请求,app 会按照顺序先运行中间件,然后再运行业务逻辑中 context.after(hook) 动态添加的 hooks,最后运行内置的 respond 函数,将请求结果自动响应的客户端。由于 Toa 没有 级联(Cascading),这些中间件或模块的运行不会有任何交叉,它们总是先运行完一个,再运行下一个。
Toa 只有一个极简的内核,提供快捷的 HTTP 操作和异步流程控制能力。具体的业务功能逻辑则由中间件和模块组合实现。用户则可根据自己的业务需求,以最轻量级的方式组合自己的应用。
让我们来看看 Toa 极其简单的 Hello World 应用程序:
const Toa = require('toa')
const app = new Toa()
app.use(function () {
this.body = 'Hello World!\n-- toa'
})
app.listen(3000)
Class: Toa()
Class: Toa(server)
Class: Toa(options)
Class: Toa(onerror)
Class: Toa(server, options)
server: {Object}, http server 或 https server 实例。options: {Object} 类似thunks的 options,对于 server 的每一个 client request,toa app 均会用thunks生成一个的thunk,挂载到context.thunk,该thunk的作用域对该 client request 的整个生命周期生效。options.onerror: {Function} 其this为 client request 的context对象。当 client request 处理流程出现异常时,会抛出到onerror,原有处理流程会终止,onerror运行完毕后再进入 toa 内置的异常处理流程,最后respond客户端。如果onerror返回true,则会忽略该异常,异常不会进入内置异常处理流程,然后直接respond客户端。
// with full arguments
const app = new Toa(server, {
onerror: function (error) {}
})
app.keys = ['key1', 'key2']
设置 cookie 签名密钥,参考 Keygrip。
注意,签名密钥只在配置项 signed 参数为真时才会生效:
this.cookies.set('name', 'test', {signed: true})
app.config = config
config 会被 context.config 继承,但 context.config 不会修改 app.config。
app.config = config
app.config 默认值:
{
proxy: false, // 决定了哪些 `proxy header` 参数会被加到信任列表中
env: process.env.NODE_ENV || 'development', // node 执行环境
subdomainOffset: 2,
poweredBy: 'Toa',
secureCookie: null
}
app.use(function () {})
app.use(function (callback) {})
app.use(function * () {})
app.use(async function () {})
加载中间件,返回 app,fn 必须是 thunk 函数或 generator 函数,函数中的 this 值为 context。
app.use(function (callback) {
// task
// this === context
callback(err, result)
})
app.use(function * () {
// task
// this === context
yield result
})
app.onerror = function (error) {}
设置 onerror 函数,当 app 捕捉到程序运行期间的错误时,会先使用 options.onerror(若提供)处理,再使用内置的 onResError 函数处理响应给客户端,最后抛出给 app.onerror 处理,应用通常可以在这里判断错误类型,根据情况将错误写入日志系统。
// default
app.onerror = function (err) {
// ignore null and response error
if (err == null || (err.status && err.status < 500)) return
if (!util.isError(err)) err = new Error('non-error thrown: ' + err)
// catch system error
let msg = err.stack || err.toString()
console.error(msg.replace(/^/gm, ' '))
}
app.toListener()
返回 app request listener。
const http = require('http')
const toa = require('toa')
const app = toa()
const server = http.createServer(app.toListener())
server.listen(3000)
等效于:
const toa = require('toa')
const app = toa()
app.listen(3000)
app.listen(port, [hostname], [backlog], [callback])
app.listen(path, [callback])
app.listen(handle, [callback])
返回 server,用法与 httpServer.listen 一致。
// 与 httpServer.listen 一致
app.listen(3000)