如何在装饰器中获取参数名?实现方法参数校验装饰器求助
实现参数校验装饰器来精简你的代码
完全理解你的需求——用装饰器把重复的参数校验逻辑抽离出来,让业务代码更清爽对吧?咱们可以先搞一个通用的枚举参数校验装饰器,这样不仅能复用在orderBy和sort上,以后其他需要枚举校验的参数也能直接用。
第一步:创建通用的枚举校验装饰器
不管是orderBy还是sort,本质都是校验参数是否在指定的合法值列表里,所以咱们写一个装饰器工厂函数,接受参数名和合法值数组,返回对应的装饰器:
// TypeScript版本,带类型注解更严谨 function ValidateEnum(paramName: string, allowedValues: string[]) { return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) { const originalMethod = descriptor.value; descriptor.value = function(...args: any[]) { // 匹配方法参数的索引(对应getUsers的参数顺序:offset, limit, orderBy, sort) const paramIndex = ['offset', 'limit', 'orderBy', 'sort'].indexOf(paramName); if (paramIndex === -1) { throw new Error(`参数 ${paramName} 不存在于方法参数列表中`); } const paramValue = args[paramIndex]; // 校验参数合法性 if (!allowedValues.includes(paramValue)) { // 这里返回你需要的HTTP响应,比如适配Express的格式 return { status: 400, message: `参数 ${paramName} 不合法,允许的值为:${allowedValues.join(', ')}` }; } // 校验通过,执行原方法逻辑 return originalMethod.apply(this, args); }; return descriptor; }; }
如果是纯JavaScript环境,去掉类型注解即可:
function ValidateEnum(paramName, allowedValues) { return function(target, propertyKey, descriptor) { const originalMethod = descriptor.value; descriptor.value = function(...args) { const paramIndex = ['offset', 'limit', 'orderBy', 'sort'].indexOf(paramName); if (paramIndex === -1) { throw new Error(`参数 ${paramName} 不存在于方法参数列表中`); } const paramValue = args[paramIndex]; if (!allowedValues.includes(paramValue)) { return { status: 400, message: `参数 ${paramName} 不合法,允许的值为:${allowedValues.join(', ')}` }; } return originalMethod.apply(this, args); }; return descriptor; }; }
第二步:在getUsers方法上使用装饰器
现在你只需要给getUsers方法加上两个装饰器注解,就能完成参数校验,业务代码里完全不用再写校验逻辑:
class UserController { // 可以把合法值存在类属性里,方便统一维护 private orderByFields = ['createdAt', 'updatedAt']; private sortValues = ['ASC', 'DESC']; @ValidateEnum('orderBy', ['createdAt', 'updatedAt']) @ValidateEnum('sort', ['ASC', 'DESC']) getUsers(offset: number, limit: number, orderBy: string, sort: string) { // 这里只需要写核心业务逻辑就行! console.log('参数校验通过,执行用户查询逻辑'); // ... 你的原有业务代码 } }
额外优化:动态读取合法值列表
如果你不想在装饰器里硬编码合法值,还可以改成从类实例的属性中动态获取,这样以后修改合法值只需要更新类属性,不用动装饰器:
function ValidateEnumFromInstance(paramName: string, allowedValuesKey: string) { return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) { const originalMethod = descriptor.value; descriptor.value = function(...args: any[]) { const paramIndex = ['offset', 'limit', 'orderBy', 'sort'].indexOf(paramName); if (paramIndex === -1) { throw new Error(`参数 ${paramName} 不存在于方法参数列表中`); } const paramValue = args[paramIndex]; // 从实例属性中读取合法值列表 const allowedValues = this[allowedValuesKey]; if (!allowedValues.includes(paramValue)) { return { status: 400, message: `参数 ${paramName} 不合法,允许的值为:${allowedValues.join(', ')}` }; } return originalMethod.apply(this, args); }; return descriptor; }; }
使用时改成这样:
class UserController { private orderByFields = ['createdAt', 'updatedAt']; private sortValues = ['ASC', 'DESC']; @ValidateEnumFromInstance('orderBy', 'orderByFields') @ValidateEnumFromInstance('sort', 'sortValues') getUsers(offset: number, limit: number, orderBy: string, sort: string) { // ... 业务代码 } }
对比前后代码
原来你需要在方法里写:
if (!includes(this.orderByFields, orderBy)) { return HTTP响应 }
if (!includes(this.sortValues, sort)) { return HTTP响应 }
现在只需要两行装饰器注解,业务代码彻底摆脱了校验逻辑,是不是清爽太多了?
内容的提问来源于stack exchange,提问作者Clowning
相关产品推荐
相关产品推荐

