You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何解决TSOA要求传入子类中已内部设置的属性问题

如何解决TSOA要求传入子类中已内部设置的属性问题

我之前也被TSOA这个“多管闲事”的特性坑过,刚好有两个非常实用的解决方案,既能解决「要求传type字段」的问题,又能堵住「type可以随便设」的漏洞,咱们一个个说:

方案一:用@Hidden()装饰器直接隐藏父类属性

TSOA自带了@Hidden()装饰器,专门用来把某个属性从生成的API Schema和请求校验规则中移除。咱们只需要给父类的type属性加上这个装饰器就行:

import { Hidden } from 'tsoa';

export class BaseProvenance {
  public id: string;
  
  // 标记type为隐藏字段,TSOA就不会要求请求体里传它了
  @Hidden()
  public type: ProvenanceObjectType;
  
  public metadata: Record<string, any>;

  constructor(id: string, type: ProvenanceObjectType, metadata: Record<string, any> = {}) {
    this.id = id;
    this.type = type;
    this.metadata = metadata;
  }
}

这样修改后:

  1. TSOA生成的Swagger文档里,请求体结构会自动去掉type字段,前端不用再传;
  2. 请求校验时不会因为缺少type抛出错误;
  3. 子类构造器里硬编码的type还是会正常赋值,完全不影响后续的日志或业务逻辑。

小补充:如果担心用户主动传错误的type,可以在保存数据前强制修正它(比如在insertProvenanceNode函数里根据实体类型重置type),彻底堵住篡改漏洞。

方案二:用DTO和实体类分离(更健壮的长期方案)

如果你的业务逻辑比较复杂,或者以后可能要修改请求/实体结构,推荐用「请求DTO」和「业务实体」彻底分离的方式,这也是我现在更常用的方案:

  • 专门创建一个用于接收请求的DTO类,里面只包含前端需要传的字段(完全没有type);
  • 在控制器里手动把DTO转换成业务实体实例,这时候我们自己控制type的赋值。

1. 创建请求DTO

// 只定义前端需要传的字段,和业务实体解耦
export class CreateEntityDto {
  public id: string;
  public metadata: Record<string, any> = {};
}

2. 修改控制器逻辑

@Post('entities')
@Security("jwt", ["admin"])
// 用DTO接收请求体,TSOA只会校验DTO的结构
public async postEntity(@Body() entityDto: CreateEntityDto): Promise<Entity> {
  // 手动把DTO转成Entity实例,自动设置正确的type
  const entity = new Entity(entityDto.id, entityDto.metadata);
  // 再执行保存逻辑
  return insertProvenanceNode<Entity>(entity);
}

这个方案的核心优势:

  • 彻底解耦请求结构和业务实体,以后不管实体怎么改,请求DTO可以独立调整,维护成本更低;
  • 完全杜绝了用户传错type的可能,因为type是后端硬编码设置的,用户根本碰不到这个字段;
  • TSOA的校验逻辑只针对DTO,请求体里完全不需要type,自然不会有校验错误。

额外补充:彻底防止type被篡改

如果用了方案一,担心用户主动传错误的type,可以在保存数据前强制修正:

async function insertProvenanceNode<T extends BaseProvenance>(entity: T): Promise<T> {
  // 根据实体的构造函数,强制设置正确的type
  if (entity instanceof Entity) {
    entity.type = ProvenanceObjectType.Entity;
  } else if (entity instanceof Agent) {
    entity.type = ProvenanceObjectType.Agent;
  } else if (entity instanceof Activity) {
    entity.type = ProvenanceObjectType.Activity;
  }

  // 执行数据库保存逻辑
  await yourDatabaseClient.save(entity);
  return entity;
}

这样就算用户恶意传了错误的type,后端也会自动修正,保证数据的正确性。

如果只是快速解决问题,方案一足够简单直接;如果追求长期的代码可维护性,方案二更值得推荐~

火山引擎 最新活动