- 新增装饰器支持(@Get, @Post, @Put, @Delete, @Patch, @Controller) - 实现路由自动注册机制(RouteScanner) - 添加全局异常处理中间件(Exception) - 实现统一响应体格式(ApiResponse) - 新增请求体解析中间件(BodyParser) - 重构控制器为类模式,支持装饰器路由 - 添加示例用户控制器(UserController) - 更新TypeScript配置支持装饰器 - 添加reflect-metadata依赖 - 完善项目文档 Breaking Changes: - 控制器现在返回数据而不是直接设置ctx.body - 新增统一的API响应格式
139 lines
3.4 KiB
TypeScript
139 lines
3.4 KiB
TypeScript
import type Koa from 'koa';
|
||
import type { Middleware } from './types.ts';
|
||
|
||
/**
|
||
* 统一响应体结构
|
||
*/
|
||
export interface ApiResponse<T = any> {
|
||
code: number; // 状态码:0表示成功,其他表示失败
|
||
message: string; // 响应消息
|
||
data?: T; // 响应数据
|
||
timestamp: number; // 时间戳
|
||
}
|
||
|
||
/**
|
||
* 自定义业务异常类
|
||
*/
|
||
export class BusinessError extends Error {
|
||
public code: number;
|
||
public httpStatus: number;
|
||
|
||
constructor(message: string, code = 1000, httpStatus = 400) {
|
||
super(message);
|
||
this.name = 'BusinessError';
|
||
this.code = code;
|
||
this.httpStatus = httpStatus;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 全局异常处理中间件
|
||
*/
|
||
export class Exception implements Middleware {
|
||
apply(app: Koa): void {
|
||
app.use(async (ctx, next) => {
|
||
try {
|
||
await next();
|
||
|
||
// 如果没有设置响应体,则返回404
|
||
if (ctx.status === 404 && !ctx.body) {
|
||
this.sendResponse(ctx, 404, '接口不存在', null, 404);
|
||
}
|
||
} catch (error) {
|
||
console.error('全局异常捕获:', error);
|
||
this.handleError(ctx, error);
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 处理错误
|
||
*/
|
||
private handleError(ctx: Koa.Context, error: any): void {
|
||
if (error instanceof BusinessError) {
|
||
// 业务异常
|
||
this.sendResponse(ctx, error.code, error.message, null, error.httpStatus);
|
||
} else if (error.status) {
|
||
// Koa HTTP 错误
|
||
const message = error.status === 401 ? '未授权访问' :
|
||
error.status === 403 ? '禁止访问' :
|
||
error.status === 404 ? '资源不存在' :
|
||
error.status === 422 ? '请求参数错误' :
|
||
error.message || '请求失败';
|
||
|
||
this.sendResponse(ctx, error.status, message, null, error.status);
|
||
} else {
|
||
// 系统异常
|
||
const isDev = process.env.NODE_ENV === 'development';
|
||
const message = isDev ? error.message : '服务器内部错误';
|
||
const data = isDev ? { stack: error.stack } : null;
|
||
|
||
this.sendResponse(ctx, 500, message, data, 500);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 发送统一响应
|
||
*/
|
||
private sendResponse(
|
||
ctx: Koa.Context,
|
||
code: number,
|
||
message: string,
|
||
data: any = null,
|
||
httpStatus = 200
|
||
): void {
|
||
const response: ApiResponse = {
|
||
code,
|
||
message,
|
||
data,
|
||
timestamp: Date.now()
|
||
};
|
||
|
||
ctx.status = httpStatus;
|
||
ctx.body = response;
|
||
ctx.type = 'application/json';
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 创建成功响应的辅助函数
|
||
*/
|
||
export function createSuccessResponse<T>(data: T, message = '操作成功'): ApiResponse<T> {
|
||
return {
|
||
code: 0,
|
||
message,
|
||
data,
|
||
timestamp: Date.now()
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 创建成功响应的辅助函数(自动设置消息)
|
||
*/
|
||
export function createAutoSuccessResponse<T>(data: T): ApiResponse<T> {
|
||
let message = '操作成功';
|
||
|
||
// 根据数据类型自动生成消息
|
||
if (Array.isArray(data)) {
|
||
message = `获取列表成功,共${data.length}条记录`;
|
||
} else if (data && typeof data === 'object') {
|
||
message = '获取数据成功';
|
||
} else if (data === null || data === undefined) {
|
||
message = '操作完成';
|
||
}
|
||
|
||
return createSuccessResponse(data, message);
|
||
}
|
||
|
||
/**
|
||
* 创建失败响应的辅助函数
|
||
*/
|
||
export function createErrorResponse(code: number, message: string, data?: any): ApiResponse {
|
||
return {
|
||
code,
|
||
message,
|
||
data,
|
||
timestamp: Date.now()
|
||
};
|
||
}
|