koatty_exception
v2.1.5
Published
Exception handler for koatty.
Downloads
786
Maintainers
Readme
koatty_exception
koatty_exception 是 Koatty 框架的异常处理模块,提供了统一的错误处理机制、响应格式化和多协议支持。
✨ 特性
- 🎯 统一异常处理 - 提供标准化的异常处理机制
- 🔗 链式调用 - 支持方法链式调用,代码更优雅
- 🌐 多协议支持 - 支持 HTTP、gRPC、WebSocket 多种协议
- 📊 可观测性 - 集成 OpenTelemetry 链路追踪
- 🔧 高度可配置 - 支持自定义日志格式、错误响应格式等
- 📝 TypeScript 支持 - 完整的类型定义和类型安全
- 🚀 零依赖核心 - 核心功能无外部依赖
- 📦 装饰器模式 - 使用
@ExceptionHandler装饰器注册异常处理器
📦 安装
npm install koatty_exception
# 或使用 yarn
yarn add koatty_exception
# 或使用 pnpm
pnpm add koatty_exception🚀 快速开始
基础用法
import { Exception, Output, CommonErrorCode } from 'koatty_exception';
// 创建基础异常
const error = new Exception('用户未找到', CommonErrorCode.RESOURCE_NOT_FOUND, 404);
// 链式调用设置异常属性
const customError = new Exception('验证失败')
.setCode(CommonErrorCode.VALIDATION_ERROR)
.setStatus(400)
.setContext({
requestId: 'req-123',
path: '/api/users',
method: 'POST'
});
// 使用 Output 类格式化响应
const successResponse = Output.ok('操作成功', { id: 1, name: '张三' });
const errorResponse = Output.fail('操作失败', null, 1001);自定义异常处理器
import { Exception, ExceptionHandler } from 'koatty_exception';
import { KoattyContext } from 'koatty_core';
@ExceptionHandler()
export class ValidationException extends Exception {
constructor(message: string, field?: string) {
super(message, CommonErrorCode.VALIDATION_ERROR, 400);
if (field) {
this.setContext({ field });
}
}
async handler(ctx: KoattyContext): Promise<any> {
// 自定义处理逻辑
const response = {
error: 'VALIDATION_ERROR',
message: this.message,
field: this.context?.field,
timestamp: new Date().toISOString()
};
ctx.status = this.status;
ctx.type = 'application/json';
return ctx.res.end(JSON.stringify(response));
}
}
// 使用自定义异常
throw new ValidationException('邮箱格式不正确', 'email');全局配置
import { setExceptionConfig } from 'koatty_exception';
// 配置异常处理行为
setExceptionConfig({
enableStackTrace: process.env.NODE_ENV !== 'production',
logFormat: 'json',
maxStackLength: 1000,
customErrorFormat: (error) => ({
errorId: generateErrorId(),
type: error.constructor.name,
message: error.message,
timestamp: new Date().toISOString()
})
});📚 API 文档
Exception 类
构造函数
constructor(message: string, code?: number, status?: number, stack?: string, span?: Span)方法
setCode(code: number): this
设置错误代码(必须为非负整数)
setStatus(status: number): this
设置 HTTP 状态码(必须在 100-599 范围内)
setMessage(message: string): this
设置错误消息
setContext(context: Partial): this
设置错误上下文信息
setSpan(span: Span): this
设置 OpenTelemetry 跟踪 span
async handler(ctx: KoattyContext): Promise
处理异常并返回响应
toJSON(): object
将异常转换为普通对象
Output 类
静态方法
ok(message: string | JsonResult, data?: T, code?: number): JsonResult
创建成功响应
// 基础用法
Output.ok('操作成功', { id: 1 });
// 结果: { code: 0, message: '操作成功', data: { id: 1 } }
// 自定义代码
Output.ok('创建成功', newUser, 201);fail(err?: Error | CodeError | string | unknown, data?: T, code?: number): JsonResult
创建失败响应
// 使用错误对象
Output.fail(new Error('数据库连接失败'));
// 使用字符串
Output.fail('参数无效', null, 1001);
// 使用 CodeError 对象
Output.fail({ code: 1002, message: '权限不足', data: { required: 'admin' } });paginate(items: T[], total: number, page: number, pageSize: number, message?: string): JsonResult
创建分页响应
const users = [/* 用户列表 */];
const response = Output.paginate(users, 100, 2, 10, '用户列表');
// 包含 pagination 信息:{ total, page, pageSize, totalPages, hasNext, hasPrev }withMeta(message: string, data: T, meta?: Record<string, unknown>, code?: number): JsonResult
创建带元数据的响应
Output.withMeta('查询成功', userData, {
version: '1.0',
executionTime: 120,
cached: true
});工具函数
prevent(): Promise
创建一个用于阻止下一步处理的 Promise
isPrevent(err: unknown): err is Error
检查是否为阻止处理的错误
isException(err: unknown): err is Exception
类型保护函数,检查是否为 Exception 实例
toSafeError(err: unknown): Error
将任意值转换为安全的 Error 对象
isNetworkError(err: unknown): boolean
检查是否为网络相关错误
🔧 配置选项
interface ExceptionConfig {
enableStackTrace?: boolean; // 是否启用堆栈跟踪(默认:非生产环境启用)
logFormat?: 'json' | 'text'; // 日志格式(默认:json)
customErrorFormat?: (error: Exception) => any; // 自定义错误格式化函数
maxStackLength?: number; // 最大堆栈长度(默认:1000)
}🏗️ 高级用法
1. 自定义业务异常
@ExceptionHandler()
export class BusinessException extends Exception {
constructor(message: string, businessCode: string) {
super(message, CommonErrorCode.GENERAL_ERROR, 400);
this.setContext({ businessCode });
}
}
@ExceptionHandler()
export class AuthenticationException extends Exception {
constructor(message: string = '身份验证失败') {
super(message, CommonErrorCode.AUTHENTICATION_ERROR, 401);
}
async handler(ctx: KoattyContext): Promise<any> {
// 清除认证信息
ctx.cookies.set('token', null);
return super.handler(ctx);
}
}2. 错误聚合和统计
export class ErrorCollector {
private static errors: Map<string, number> = new Map();
static record(error: Exception): void {
const key = `${error.constructor.name}:${error.code}`;
this.errors.set(key, (this.errors.get(key) || 0) + 1);
}
static getStats(): Record<string, number> {
return Object.fromEntries(this.errors);
}
}
// 在异常处理器中使用
@ExceptionHandler()
export class MonitoredException extends Exception {
async handler(ctx: KoattyContext): Promise<any> {
ErrorCollector.record(this);
return super.handler(ctx);
}
}3. 异步错误处理
import { prevent, isPrevent } from 'koatty_exception';
async function processData() {
try {
const shouldStop = await checkCondition();
if (shouldStop) {
await prevent(); // 阻止后续处理
}
// 继续处理...
} catch (error) {
if (isPrevent(error)) {
console.log('处理被阻止');
return;
}
throw error; // 重新抛出其他错误
}
}🧪 测试
# 运行所有测试
npm test
# 运行测试并生成覆盖率报告
npm run test:cov
# 运行 ESLint 检查
npm run eslint📊 错误代码参考
export enum CommonErrorCode {
SUCCESS = 0, // 成功
GENERAL_ERROR = 1, // 通用错误
VALIDATION_ERROR = 1001, // 验证失败
AUTHENTICATION_ERROR = 1002, // 身份验证失败
AUTHORIZATION_ERROR = 1003, // 权限不足
RESOURCE_NOT_FOUND = 1004, // 资源未找到
RESOURCE_CONFLICT = 1005, // 资源冲突
RATE_LIMIT_EXCEEDED = 1006, // 请求频率超限
INTERNAL_SERVER_ERROR = 1007, // 内部服务器错误
SERVICE_UNAVAILABLE = 1008, // 服务不可用
TIMEOUT_ERROR = 1009, // 超时错误
NETWORK_ERROR = 1010 // 网络错误
}🤝 贡献
欢迎贡献代码!请查看 贡献指南 了解详情。
开发步骤
- Fork 项目
- 创建特性分支 (
git checkout -b feature/amazing-feature) - 提交更改 (
git commit -m 'Add some amazing feature') - 推送到分支 (
git push origin feature/amazing-feature) - 创建 Pull Request
📄 许可证
本项目基于 BSD-3-Clause 许可证开源。
🔗 相关链接
🙏 致谢
感谢所有为这个项目做出贡献的开发者!
