小满nestjs(第五章 装饰器进阶-封装可复用的GET请求装饰器)

发布时间:2026/5/15 23:51:37

小满nestjs(第五章 装饰器进阶-封装可复用的GET请求装饰器) 1. 从基础GET装饰器到可复用封装还记得上一章我们实现的简单GET请求装饰器吗那个只能算是个玩具。在实际项目中我们需要处理各种复杂场景不同的请求头、错误重试机制、日志记录、性能监控...如果每个控制器都写一遍这些逻辑代码很快就会变得难以维护。我刚开始用NestJS做电商后台时就遇到过这种问题。系统需要对接十几个外部API每个接口都要处理超时、鉴权、日志。最初的做法是复制粘贴代码结果某天需要修改请求头格式时我不得不修改二十多个文件——这简直是一场噩梦。后来发现装饰器才是解决这类问题的银弹。通过将通用逻辑封装到装饰器中不仅能保持代码整洁还能实现跨项目的复用。比如我们现在要封装的这个GET装饰器经过多次迭代后已经在我们团队三个不同项目中稳定运行了半年多。2. 可配置装饰器的核心设计2.1 装饰器工厂模式进阶基础版的装饰器工厂只能接收URL参数而我们的增强版需要支持完整配置项interface GetOptions { url: string; headers?: Recordstring, string; timeout?: number; retry?: number; logger?: boolean; }这个配置接口的设计有几点值得注意必填的只有url字段其他都是可选配置headers使用键值对结构方便扩展各种认证信息timeout单位是毫秒默认可以设为3000retry表示重试次数对于重要接口建议设为2-3次logger开关控制是否记录请求日志2.2 实现配置合并逻辑实际项目中我们通常会有全局配置和局部配置const DEFAULT_OPTIONS { timeout: 3000, retry: 0, logger: true }; function mergeOptions(options: GetOptions) { return { ...DEFAULT_OPTIONS, ...options }; }这种合并策略让我们的装饰器既灵活又统一。全局配置放在DEFAULT_OPTIONS中单个装饰器调用时可以覆盖特定配置。3. 增强型GET装饰器实现3.1 核心功能实现完整版的装饰器需要处理以下场景const Get (options: GetOptions | string): MethodDecorator { return (target, key, descriptor) { const originalMethod descriptor.value; const config typeof options string ? mergeOptions({ url: options }) : mergeOptions(options); descriptor.value async function (...args: any[]) { try { const response await axios.get(config.url, { headers: config.headers, timeout: config.timeout }); return originalMethod.apply(this, [response.data, ...args]); } catch (error) { if (config.retry 0) { // 重试逻辑实现 } throw error; } }; }; };这个版本有几个关键改进支持字符串参数快捷调用保持向下兼容使用async/await替代Promise链式调用保留了原方法的this绑定响应数据自动解构3.2 错误处理增强在实际项目中简单的错误捕获远远不够。我们需要区分网络错误、超时错误、业务错误等不同情况enum ErrorType { NETWORK, TIMEOUT, BUSINESS } // 在catch块中添加错误分类 const classifiedError classifyError(error); if (config.logger) { logError(classifiedError, config); }完整的错误分类函数需要考虑axios错误码、响应状态码等多种因素。这部分代码虽然繁琐但对后续的错误监控非常重要。4. 装饰器的高级用法4.1 组合装饰器模式在真实项目中我们经常需要组合多个装饰器。比如同时需要缓存和请求Cache({ ttl: 60 }) Get(/api/products) async getProducts() { // 方法体会被两个装饰器增强 }实现这种效果需要注意装饰器的执行顺序从下到上从右到左。我们的GET装饰器需要确保与其他装饰器兼容。4.2 元数据集成配合reflect-metadata可以实现更强大的功能import reflect-metadata; const GetWithMeta (options: GetOptions): MethodDecorator { return (target, key) { Reflect.defineMetadata(api:endpoint, options, target, key); // 其他装饰逻辑... }; };这样我们就能在全局统一获取所有API端点信息非常适合用来生成API文档或做接口监控。5. 实战中的性能优化5.1 请求拦截器优化当装饰器被大量使用时每个请求都创建新的axios实例会导致性能问题。解决方案是使用单例的axios实例const apiClient axios.create({ timeout: 3000 }); // 在装饰器内部复用这个实例 apiClient.get(config.url);5.2 缓存策略实现对于高频调用的接口可以在装饰器层面实现缓存const cacheStore new Map(); descriptor.value async function(...args) { const cacheKey generateCacheKey(config.url, args); if (cacheStore.has(cacheKey)) { return cacheStore.get(cacheKey); } // ...正常请求逻辑 cacheStore.set(cacheKey, result); return result; };注意要设置合理的缓存过期时间避免内存泄漏。6. 测试与调试技巧6.1 单元测试方案测试装饰器需要特殊技巧因为装饰器本质上是修改类行为。推荐使用以下测试结构describe(Get Decorator, () { let testInstance: TestClass; beforeAll(() { Controller() class TestClass { Get(https://api.example.com) testMethod() {} } testInstance new TestClass(); }); it(should modify method behavior, () { // 测试方法是否被正确增强 }); });6.2 调试技巧调试装饰器时最头疼的问题是代码执行顺序。我常用的方法是在装饰器开始和结束处打日志使用VS Code的调试器在装饰器函数内设置断点检查descriptor.value.toString()查看方法是否被正确修改7. 在NestJS中的最佳实践虽然我们实现的装饰器可以在任何TS项目中使用但在NestJS中有更优雅的集成方式7.1 作为Provider使用将装饰器封装成可注入的服务Injectable() export class ApiClient { async get(config: GetOptions) { // 实现核心逻辑 } } // 然后在装饰器中使用 constructor(private readonly apiClient: ApiClient) {} GetDecorator(/api) async getData() { // ... }7.2 结合NestJS拦截器对于需要全局处理的逻辑如认证可以结合拦截器使用UseInterceptors(LoggingInterceptor) GetDecorator(/api) async getData() { // ... }这种分层设计让代码更加清晰可维护。8. 从装饰器到自定义装饰器工厂当项目规模扩大时我们需要更高级的抽象——装饰器工厂函数function createApiDecorator(baseOptions: GetOptions) { return (overrideOptions: PartialGetOptions) { return Get({ ...baseOptions, ...overrideOptions }); }; } // 项目初始化时配置 const ProjectGet createApiDecorator({ timeout: 5000, headers: { X-App-Version: 1.0.0 } }); // 控制器中使用 Controller() class UserController { ProjectGet({ url: /api/users }) getUsers() {} }这种模式特别适合大型项目可以确保所有API调用遵循统一的配置标准。

相关新闻