如何扩展@Query()的PageOptionsDto以支持未知过滤属性?
解决方案
要在保持PageOptionsDto通用性的同时,通过URL查询参数传递类型安全的过滤条件,推荐用交叉类型结合业务实体专属的过滤Dto来实现,具体步骤如下:
1. 为每个业务实体定义专属过滤Dto
针对每个需要过滤的表格(比如Player表格),单独创建一个过滤Dto,定义该实体的过滤字段及验证规则:
import { ApiPropertyOptional } from '@nestjs/swagger'; import { IsOptional, IsString, IsInt } from 'class-validator'; import { Type } from 'class-transformer'; export class PlayerFilterDto { @ApiPropertyOptional({ description: '玩家名称模糊匹配' }) @IsOptional() @IsString() readonly name?: string; @ApiPropertyOptional({ description: '玩家年龄精确匹配' }) @IsOptional() @IsInt() @Type(() => Number) readonly age?: number; // 其他需要过滤的字段都在这里定义 }
2. 在控制器中使用交叉类型组合Dto
在你的控制器方法里,将通用的PageOptionsDto与业务专属的PlayerFilterDto通过交叉类型&组合,作为Query参数的类型:
@Get('player/table') async getPlayerTable( @Query() pageOptionsAndFilters: PageOptionsDto<PlayerSortColumn> & PlayerFilterDto, ) { // 直接访问分页排序参数:pageOptionsAndFilters.page、pageOptionsAndFilters.take等 // 同时类型安全地访问过滤参数:pageOptionsAndFilters.name、pageOptionsAndFilters.age等 // 示例:提取参数处理业务逻辑 const { skip, take, order, orderColumn, name, age } = pageOptionsAndFilters; const filterConditions = {}; if (name) filterConditions['name'] = new RegExp(name, 'i'); // 模糊匹配 if (age) filterConditions['age'] = age; // 精确匹配 // 执行查询:分页+排序+过滤 const result = await this.playerRepository.find({ where: filterConditions, skip, take, order: { [orderColumn]: order }, }); // ...返回结果 }
3. 保持PageOptionsDto的通用性
你的原始PageOptionsDto完全不需要修改,依然可以复用在所有表格的分页排序场景中,每个表格只需要新增对应的过滤Dto即可。
方案优势
- 类型安全:避免使用
any,过滤字段的类型、验证规则都能通过Dto严格约束 - 通用性保留:
PageOptionsDto无需硬编码任何业务字段,依然是通用的分页排序工具 - 符合URL查询参数习惯:过滤参数直接以平级的方式放在URL中(比如
?name=ni&age=20),和你现有的分页参数格式统一
内容的提问来源于stack exchange,提问作者sirzento
相关产品推荐
相关产品推荐

