在没有任何额外处理场景下,func 会默认抛出内置的 Exception,我们可以根据业务需求将异常扭转至需要的输出流,或是自行抑制异常。
Exception 与默认行为
内置 Exception 为用户提供稳定的 code、scope、details、message 和 cause 定义,但不提供 JSON wire format。你可以跟随此风格抛出错误或是直接使用原生 Error。
可修正的命令行输入错误和 Handler 抛出的普通错误默认设置退出码 1,并向 stderr 写入一行普通文本。默认错误路由不添加颜色、边框或表格。系统定义错误、Provider 初始化错误等框架故障 (F_SYSTEM) 会继续抛出,除非应用 Filter 明确捕获。
捕获与处理错误
Command 或 Module 实现 OnError 后,就可以直接处理自身作用域内的错误。onError 是固定方法,不是生命周期方法,也不使用参数装饰器注入;框架始终传入 Exception 和本次调用的 Context。
import { Command, Handler } from 'func'
import type { Context, Exception, OnError } from 'func'
@Command('publish')
export class PublishCommand implements OnError {
@Handler()
run() {
throw new Error('Registry is unavailable')
}
onError(exception: Exception, context: Context) {
context.io.stderr.write(`发布失败:${exception.message}\n`)
}
}onError 正常返回表示错误已经处理,返回值会被忽略。抛出错误表示当前层无法处理,新的错误会继续进入外层 onError;当前实例不会再次捕获自己抛出的错误。
错误传播顺序
对于已经选中的命令,错误按以下顺序传播:
- 当前 Command(包括 Option Command 与 Missing Command)
- Command 的直接所属 Module
- 根模块
Module 自身的 onInit / onDispose 错误从该 Module 开始向根模块传播。Service 抛出的业务错误会沿调用它的 Command 或 Module 传播;Service 自身的生命周期错误则从拥有该 Service 的 Module 开始传播。命令尚未选中的解析错误只会进入根模块。
import { Module } from 'func'
import type { Context, Exception, OnError } from 'func'
import { PublishCommand } from './commands/publish.command'
@Module({
commands: [PublishCommand],
})
export class AppModule implements OnError {
onError(exception: Exception, context: Context) {
context.io.stderr.write(`${exception.message}\n`)
}
}Context 在解析错误发生时可能尚未完成命令解析。可先检查 context.invocation.resolved:为 false 时不要读取命令参数;为 true 时可从 context.invocation.args、context.command 或 context.options 读取已经解析的调用信息。
让错误进入 JSON 响应
func 不提供固定错误 JSON schema,为了避免产生用户预期之外的行为,也不会自动序列化 Exception 或用户使用的原生 Error。
如果根模块导入了 JsonOutputModule,它可以通过构造器注入 JsonOutput,并在 onError 中显式发送允许公开的结构化数据:
import { Module } from 'func'
import type { Exception, OnError } from 'func'
import { JsonOutput, JsonOutputModule } from 'func/json-output'
@Module({ imports: [JsonOutputModule] })
export class AppModule implements OnError {
constructor(private readonly output: JsonOutput) {}
onError(exception: Exception) {
this.output.json({
error: {
code: exception.code,
message: exception.message,
},
})
}
}这种方式由应用 (也就是用户) 自行决定是否暴露 details、cause 或 stack,或是自行拼装错误码,避免框架锁定协议或意外泄漏内部信息。
JSON 输出模块的启用方式、stdout 策略和序列化边界详见 JSON 输出。
生命周期
onInit 与 onDispose 是 func 框架的资源生命周期,我们可以在生命周期中初始化资源、释放资源、记录性能数据等。
Command / Module 甚至 Service 都可以实现生命周期方法:
import { Module } from 'func'
import type { OnDispose, OnInit } from 'func'
@Module({})
export class AppModule implements OnInit, OnDispose {
onInit() {
// 本次调用已经初始化。
}
onDispose() {
// 释放当前实例拥有的资源。
}
}生命周期方法不接收本次调用的结果,返回值也会被忽略。