作为一名长期使用 Egg.js 开发企业级应用的全栈工程师,我发现很多开发者在进阶功能使用上存在误区。今天我将通过实战案例,详细解析 Egg.js 中三个核心进阶特性:参数校验规范实现、AOP 切面编程的工程化应用,以及高可靠异步任务处理方案。
在 Web 开发中,客户端传入的参数往往不可信任。我曾经历过一次线上事故:由于未对分页参数做校验,攻击者传入 pageSize=1000000 导致数据库过载。规范的参数校验能:
Egg.js 官方推荐使用 AJV (Another JSON Schema Validator) 进行校验。其优势在于:
typescript复制// 定义参数 schema
const UserCreateSchema = Type.Object({
username: Type.String({
minLength: 4,
maxLength: 20,
pattern: '^[a-zA-Z0-9_]+$'
}),
age: Type.Integer({ minimum: 18 }),
email: Type.String({ format: 'email' })
});
// 在 Controller 中使用
@HTTPController()
export class UserController {
@Inject()
private readonly ajv: Ajv;
@HTTPMethod({
method: 'POST',
path: '/users'
})
async create(userData: UserCreateType) {
this.ajv.validate(UserCreateSchema, userData);
// 业务逻辑...
}
}
AjvInvalidParamError,返回标准错误格式app/schema 目录ajv.addKeyword() 扩展业务规则typescript复制// 自定义手机号校验规则
ajv.addKeyword({
keyword: 'isMobile',
validate: (schema: boolean, data: string) => {
return /^1[3-9]\d{9}$/.test(data);
}
});
code复制[客户端请求]
|
v
[Controller] ---> [Service] ---> [Model]
| | |
v v v
[日志记录] [事务管理] [缓存处理]
typescript复制@Advice()
export class TransactionAdvice implements IAdvice {
@Inject()
private dataSource: DataSource;
async around(ctx: AdviceContext, next: () => Promise<any>) {
const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();
try {
const result = await next();
await queryRunner.commitTransaction();
return result;
} catch (error) {
await queryRunner.rollbackTransaction();
throw error;
} finally {
await queryRunner.release();
}
}
}
// 使用示例
@SingletonProto()
export class OrderService {
@Pointcut(TransactionAdvice)
async createOrder(orderData: OrderDTO) {
// 业务逻辑...
}
}
typescript复制@Advice()
export class PerformanceAdvice implements IAdvice {
@Inject()
private metrics: MetricsService;
async around(ctx: AdviceContext, next: () => Promise<any>) {
const start = Date.now();
try {
return await next();
} finally {
const cost = Date.now() - start;
this.metrics.record(
`${ctx.that.constructor.name}.${String(ctx.method)}`,
cost
);
}
}
}
typescript复制@SingletonProto()
export class ReportService {
@Inject()
private backgroundTaskHelper: BackgroundTaskHelper;
async generateReport(userId: string) {
// 设置10秒超时
this.backgroundTaskHelper.timeout = 10000;
this.backgroundTaskHelper.run(async () => {
const report = await this.buildComplexReport(userId);
await this.sendEmail(userId, report);
});
}
}
typescript复制this.backgroundTaskHelper.run(async (ctx) => {
try {
await doSomething();
} catch (error) {
if (ctx.retryCount < 3) {
ctx.retryAfter(1000); // 1秒后重试
} else {
await this.errorHandler(error);
}
}
});
现象:校验规则未触发
排查步骤:
ajv.validate() 是否被调用可能原因:
@Advice() 装饰器解决方案:
typescript复制// 持久化任务示例
this.backgroundTaskHelper.run(async () => {
const task = await this.taskRepo.create({ status: 'processing' });
try {
await processTask(task);
await this.taskRepo.update(task.id, { status: 'completed' });
} catch (error) {
await this.taskRepo.update(task.id, { status: 'failed' });
}
});
order 属性控制切面执行顺序typescript复制// 切面排序示例
@Advice({ order: 100 })
export class LogAdvice implements IAdvice {
// ...
}
@Advice({ order: 200 })
export class TransactionAdvice implements IAdvice {
// ...
}
在实际项目开发中,合理运用这些进阶特性可以显著提升代码质量和可维护性。特别是在微服务架构下,规范的参数校验和统一的切面处理能大幅降低系统间的集成成本。对于异步任务,建议结合消息队列实现更可靠的分布式任务处理。