news 2026/5/20 14:46:52

别再死记硬背了!用这5个真实场景,彻底搞懂NestJS装饰器的用法

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
别再死记硬背了!用这5个真实场景,彻底搞懂NestJS装饰器的用法

别再死记硬背了!用这5个真实场景,彻底搞懂NestJS装饰器的用法

在NestJS开发中,装饰器(Decorator)就像瑞士军刀一样,能优雅地解决各种架构问题。但很多开发者仅仅停留在"知道语法"层面,面对实际业务需求时依然无从下手。本文将带你跳出枯燥的API文档,通过5个真实场景感受装饰器如何提升代码质量。

1. 请求参数验证与转换的艺术

想象一个用户注册接口需要处理以下数据:

class CreateUserDto { username: string; password: string; birthDate: Date; }

传统验证方式需要在方法体内写大量if-else判断,而使用装饰器可以这样实现:

import { IsString, IsDateString, MinLength } from 'class-validator'; class CreateUserDto { @IsString() @MinLength(3) username: string; @IsString() @MinLength(8) password: string; @IsDateString() birthDate: Date; }

进阶技巧:自定义转换装饰器

function ToLowerCase() { return function (target: any, key: string) { let value = target[key]; Object.defineProperty(target, key, { get: () => value, set: (v: string) => { value = v.toLowerCase(); }, enumerable: true, configurable: true }); }; } class SearchDto { @ToLowerCase() keyword: string; }

提示:结合class-transformer@Transform装饰器,可以实现更复杂的数据转换逻辑

2. 自动化接口日志与性能监控

手动在每个方法里写console.log不仅低效,还会污染业务代码。方法装饰器能完美解决这个问题:

function LogExecution() { return function ( target: any, propertyKey: string, descriptor: PropertyDescriptor ) { const originalMethod = descriptor.value; descriptor.value = async function (...args: any[]) { const start = Date.now(); console.log(`[${new Date().toISOString()}] Calling ${propertyKey}`); try { const result = await originalMethod.apply(this, args); const duration = Date.now() - start; console.log(`[${new Date().toISOString()}] ${propertyKey} executed in ${duration}ms`); return result; } catch (error) { console.error(`[${new Date().toISOString()}] ${propertyKey} failed:`, error); throw error; } }; }; } class UserController { @LogExecution() async getUser(id: string) { // 业务逻辑 } }

监控指标扩展

装饰器类型采集指标存储方式
方法装饰器执行时间Prometheus
类装饰器请求量统计Elasticsearch
参数装饰器异常参数记录Sentry

3. 数据库字段映射的智能处理

TypeORM等ORM库虽然提供了基础装饰器,但实际业务中我们经常需要:

function EncryptedColumn() { return function (target: any, propertyKey: string) { const secretKey = process.env.ENCRYPTION_KEY; Object.defineProperty(target, propertyKey, { get: function() { return decrypt(this[`_${propertyKey}`], secretKey); }, set: function(value: string) { this[`_${propertyKey}`] = encrypt(value, secretKey); } }); }; } class User { @PrimaryGeneratedColumn() id: number; @Column() @EncryptedColumn() privateKey: string; }

字段映射对照表

function DbField(mapTo: string) { return function (target: any, propertyKey: string) { Reflect.defineMetadata('db:field', mapTo, target, propertyKey); }; } class Product { @DbField('product_name') name: string; @DbField('sale_price') price: number; }

4. API版本控制的优雅实现

用类装饰器实现多版本API共存:

function ApiVersion(version: string) { return function (constructor: Function) { Reflect.defineMetadata('api:version', version, constructor); }; } @Controller('users') @ApiVersion('v1') class UserControllerV1 { @Get() getUsers() { return legacyUserService.list(); } } @Controller('users') @ApiVersion('v2') class UserControllerV2 { @Get() getUsers() { return newUserService.listWithDetails(); } }

路由配置时通过metadata筛选:

const controllers = [UserControllerV1, UserControllerV2]; controllers.forEach(controller => { const version = Reflect.getMetadata('api:version', controller); const pathPrefix = `/api/${version}`; // 注册路由逻辑... });

5. 动态权限校验中间件开发

参数装饰器可以创建智能权限系统:

function RequirePermission(resource: string, action: string) { return function ( target: any, propertyKey: string, parameterIndex: number ) { const existingPermissions = Reflect.getMetadata('permissions', target, propertyKey) || []; existingPermissions.push({ index: parameterIndex, resource, action }); Reflect.defineMetadata( 'permissions', existingPermissions, target, propertyKey ); }; } class DocumentController { @Post() updateDocument( @RequirePermission('document', 'edit') userId: string, @Body() data: any ) { // 业务逻辑 } }

权限校验中间件实现:

function PermissionMiddleware(req, res, next) { const handler = req.route.handler; const permissions = Reflect.getMetadata( 'permissions', handler.constructor.prototype, handler.name ); if (permissions) { permissions.forEach(({ index, resource, action }) => { const userId = req.params[index]; if (!permissionService.check(userId, resource, action)) { throw new ForbiddenException(); } }); } next(); }

这些场景只是装饰器应用的冰山一角。在实际项目中,我经常组合使用多种装饰器类型——比如用类装饰器标记服务类别,方法装饰器处理缓存逻辑,参数装饰器做细粒度验证。这种声明式的编程方式让代码既简洁又易于维护。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/20 14:46:51

通过 Taotoken 审计日志功能回溯异常 API 调用与访问来源

🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 通过 Taotoken 审计日志功能回溯异常 API 调用与访问来源 当团队在使用大模型服务时,有时会发现账单上的 token 消耗量…

作者头像 李华
网站建设 2026/5/20 14:44:04

视频批量混剪新玩法:如何用AI一键生成千条原创带货视频?

对于做电商带货、本地生活推广或者矩阵铺量的运营团队来说,2026年最让人头疼的往往不是“没素材”,而是“素材太多剪不完”。面对成百上千个产品的原始拍摄片段,如果靠人工一个个去排列组合、加特效、配音乐,不仅效率低到令人发指…

作者头像 李华
网站建设 2026/5/20 14:42:21

2023B卷,猜密码

👨‍⚕️ 主页: gis分享者 👨‍⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍⚕️ 收录于专栏:华为OD面试 文章目录 一、🍀前言 1.1 ☘️题目详情 1.2 ☘️参考解题答案 一、🍀前言 2023B卷,猜密码 。 1.1 ☘️题目详情 题目: 小杨申请了一…

作者头像 李华
网站建设 2026/5/20 14:41:10

Arm Compiler 6的NOP指令对齐问题与代码覆盖率解决方案

1. 问题背景:Arm Compiler 6的NOP指令对齐导致的代码覆盖率问题在嵌入式开发中,代码覆盖率测试是验证软件质量的重要手段。当使用Keil MDK uVision的代码覆盖率调试功能时,我们期望覆盖率能达到100%,但实际项目中经常会遇到一个棘…

作者头像 李华