@UsePipes(ValidationPipe) 不适用于泛型(抽象控制器)

2024-04-01

我正在使用构建 APINest.js和MySQL。由于敏捷性和 DRY 原则,我正在创建一个 OOP 结构,它为给定实体(来自 TypeORM)设置所有基本 CRUD 端点。主要目标是避免为不同的实体编写相同的通用方法。

为了实现这一目标,我使用了一种策略TypeScript 泛型。我仍然需要创建所有常用文件(.controller.ts, .service.ts, .module.ts, .entity.ts)对于每个实体,但我不必编写它的方法。相反,我只是扩展了两个类:RestController and RestService。这些类已经实现了常用方法,但我必须传递一些T types作为参数,以便 TypeORM 可以将正确的存储库注入到服务中。

问题: The @UsePipes当我在父类中使用装饰器时,它没有被调用(RestController),但是当我覆盖 de 时它可以正常工作休息控制器在子类中创建方法(SubcategoriesController).

休息控制器.ts:

import { Get, Post, Body, Param, Put, Delete, UsePipes, ValidationPipe } from '@nestjs/common';
import { RestService } from './rest.service';
import { ObjectLiteral } from 'typeorm';

export abstract class RestController<T, C = T, U = T> {
  constructor(protected service: RestService<T, C, U>) {}

  @Get()
  async index(): Promise<T[]> {
    return this.service.getAll();
  }

  @Post('create')
  @UsePipes(ValidationPipe) //HERE!
  async create(@Body() data: C): Promise<T> {
    return this.service.create(data as C);
  }
}

休息.服务.ts:

import { Repository, UpdateResult, DeleteResult, Entity, DeepPartial } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';

export interface RestClass<T, C = T, U = T> {
  // Properties
  repository: Repository<T>;

  // Default Methods
  getAll(): Promise<T[]>;
  create(model: T | C | U): Promise<T>;
}

export class RestService<T, C = T, U = T> implements RestClass<T, C, U> {
  constructor(
    public repository: Repository<T>,
  ) {}

  getAll = async () => {
    return await this.repository.find({relations:: this.repository.metadata.ownRelations.map(r => r.propertyName)});
  }

  create = async (model: C) => {
    return await this.repository.save(model as C);
  }
}

以下是我如何设置真实的实体端点,扩展上述类:

子类别.controller.ts:

import { Controller, Get, Post, UsePipes, ValidationPipe, Body } from '@nestjs/common';
import { SubcategoriesService } from './subcategories.service';
import { Subcategory } from './subcategory.entity';
import { RestController } from '../rest.controller';
import { CreateSubcategoryDTO } from './dto/createSubcategory.dto';

//NOTE THE TYPE PARAMS IN <>
@Controller('subcategories')
export class SubcategoriesController extends RestController<Subcategory, CreateSubcategoryDTO> {
  constructor(public service: SubcategoriesService) {
    super(service);
  }    
}

子类别.service.ts

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Subcategory } from './subcategory.entity';
import { Repository } from 'typeorm';
import { RestService } from '../rest.service';
import { CreateSubcategoryDTO } from './dto/createSubcategory.dto';

//NOTE THE TYPE PARAMS IN <>
@Injectable()
export class SubcategoriesService extends RestService<Subcategory, CreateSubcategoryDTO> {

  constructor(
    @InjectRepository(Subcategory) repository: Repository<Subcategory>,
  ) {
    super(repository);
  }
}

创建子类别.dto.ts

import { IsString, Length, IsInt } from 'class-validator';

export class CreateSubcategoryDTO {

  @IsString()
  @Length(5, 60)
  name: string;

  @IsString()
  @Length(0, 140)
  summary: string;

  @Length(0, 140)
  icon: string;

  @IsInt()
  category: number;
}

可以看到父类接受3种类型参数:

  • T:实体
  • C:CreateDTO,可选
  • U:UpdateDTO,可选

然而,上面的代码完美地创建了端点它不验证有效负载 in the /create,正如预期的那样验证管道.

如果我覆盖 the createSubcategoriesController 中的方法,并在其中添加 UsePipes,it works!

我认为这可能是指 Nests 生命周期的错误,它可能不支持在抽象类中使用 Pipes。

有人有主意吗?

P.S。没有转译错误、lint 警告或运行时异常。


解决此问题的一种方法是为控制器创建一个工厂函数,该函数将接受您的 body param 类作为参数,然后将其传递给自定义 ValidationPipe 扩展,如下所示:

@Injectable()
export class AbstractValidationPipe extends ValidationPipe {
  constructor(
    options: ValidationPipeOptions,
    private readonly targetTypes: {
      body?: Type<any>;
      query?: Type<any>;
      param?: Type<any>;
      custom?: Type<any>;
    },
  ) {
    super(options);
  }

  async transform(value: any, metadata: ArgumentMetadata) {
    const targetType = this.targetTypes[metadata.type];
    if (!targetType) {
      return super.transform(value, metadata);
    }
    return super.transform(value, { ...metadata, metatype: targetType });
  }
}

export interface IController<T> {
  hello(body: T);
}

export function Factory<T>(bodyDto: ClassType<T>): ClassType<IController<T>> {
  @Controller()
  class ControllerHost<T> implements IController<T> {
    @Post()
    @UsePipes(new AbstractValidationPipe({whitelist: true, transform: true}, {body: bodyDto}))
    hello(@Body() body: T) {
      return "hello"
    }
  }
  return ControllerHost;
}

export class MyDto {
  @Expose()
  @IsDefined()
  @IsString()
  hello: string;
}

export class AppController extends Factory<MyDto>(MyDto) {}

没有关于 Reflection 可用的泛型的信息,因此标准 ValidationPipe 无法从中获取任何有意义的信息metadata.metatype。我正在通过为其提供可选类型参数来解决这个问题,它可以使用它来覆盖内容metadata.metatype。它有一个很好的功能,它也适用于正常用例(没有泛型)。如果你想覆盖query or param也只是通过提供适当的值targetTypes param.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

@UsePipes(ValidationPipe) 不适用于泛型(抽象控制器) 的相关文章

随机推荐

  • 如何使用 create-react-app 提供 SSL 证书?

    我正在尝试托管一个我使用 Facebook 样板在本地创建和测试的 React 应用程序 客户端应用程序与我使用 node js 制作的 API 进行交互 并且我在设置安全连接时没有遇到任何问题 使用 node js 客户端发送我的 SSL
  • Xcode 11.4 编译错误“缺少包产品 <包名称>”

    当我整合一个本地包 https developer apple com documentation xcode creating a standalone swift package with xcode在我的 Xcode 项目中 最初一切
  • 使用 JAXB 将元素表示为布尔值?

    我们有这个 XML
  • 如何使用urllib登录网站?

    我正在尝试登录这个网站 http www broadinstitute org cmap index jsp http www broadinstitute org cmap index jsp 我在 Windows 上使用 python
  • EasyMock 中“same”和“eq”的区别

    EasyMock 中的 same 和 eq 之间是否存在显着 甚至任何 差异 same检查两个对象是否实际上是同一个实例 引用相等 eq calls equals因此检查两者是否具有相同的值 值相等 请记住 默认equals实施用途 内部
  • Tensorflow 相当于 numpy.diff

    是否存在相当于的张量流numpy diff https docs scipy org doc numpy reference generated numpy diff html 计算沿给定轴的第 n 个离散差值 对于我的项目 我只需要 n
  • ARM 9处理器的opencv交叉编译

    我需要为 ARM 9 处理器交叉编译 opencv 我有处理器的工具链 但不知道如何交叉编译 请告诉我为arm板交叉编译的过程 谢谢大家 看这个参考 http www airs com ian configure configure 5 h
  • 在 Android 中使用签名 URL 从 Amazon S3 + CloudFront 播放 m3u8

    我能够使用签名 URL 访问播放列表文件 m3u8 但问题是 要访问播放列表中的流文件 ts 每个文件的 URL 也需要签名 例如 我可以使用签名 URL 访问 playlist m3u8 EXTM3U EXT X TARGETDURATI
  • 在模态 JDialog 之外时光标不正确?

    使用方法时setCursor 要更改组件使用的光标 所有组件都可以正常工作 包括JFrame and JDialog 这里的问题在于modal JDialog 当鼠标处于inside对话框中 光标显示在右侧 但是 当鼠标移动时outside
  • 使用 AngularJS 和 ngResource 调用外部 api

    我们目前正在开发一个小型 AngularJS 项目 并从前端开始 所以纯 HTML 和 JavaScript 然而 我们需要使用 ngResource 进行一些 API 调用 目前我们正在使用canned http github com s
  • 将 Google Apps API 密钥与距离矩阵结合使用

    我正在使用谷歌距离矩阵 API https developers google com maps documentation distancematrix 并且文档告诉我我需要一个 API 密钥 但我可以在没有 API 密钥的情况下使用它
  • 当 AngularJS 中的响应为 304(未修改)时,$http 返回错误

    我正在使用 angularjs http 向 asp net web api 发出请求 服务器上的 Web API 通过 ETag 管理缓存 如果我的响应状态代码是 304 那么它会进入错误函数 而不是成功函数 我不明白 AngularJS
  • 在 DataGrid 工具栏的弹出组件 Material-UI 中添加自定义样式

    我正在创建一个自定义Data Grid Toolbar通过修改现有组件Grid Toolbar组件来自材质 UI https v4 mui com Here https v4 mui com components data grid fil
  • WWW::Mechanize::Timed https 超时不起作用

    所以我在互联网上研究了这个问题 至少我是这么认为的 我正在尝试设置一个alarm超时 60 秒get 但它不会被捕获 并且会运行超过 60 秒 而且每当 www mechanized timed 构造函数达到默认超时 180 秒 时 我都会
  • Discord.py 机器人没有响应

    我对discord py相对较新 我正在制作一个机器人 但是使用某些命令或我放置的东西 机器人停止响应命令 当它以前工作时 我不知道如何修复它 机器人开启 但不响应任何命令 这是新的区域 import os import discord f
  • C#列表覆盖问题[重复]

    这个问题在这里已经有答案了 我有一些代码如下所示 用于写入 a 的值gridview到如图所示的列表 该代码确实获得了正确的值 但是当网格的第二行添加到列表中时 它会覆盖列表的第一行 有人知道为什么会发生这种情况吗 C Code List
  • 如何在 SQL 中识别连续日期组?

    我正在尝试编写一个函数来识别日期组并测量该组的大小 到目前为止 我一直在 Python 中按程序执行此操作 但我想将其移至 SQL 中 例如 列表 Bill 01 01 2011 Bill 02 01 2011 Bill 03 01 201
  • 角度标记错误

    我突然得到这个 它不允许我正确使用我的传单地图 每次我单击地图时 都会将一个标记添加到相同的坐标 当我尝试使用函数删除标记时 它会清空标记数组 但标记在地图上仍然可见 这是怎么回事 Error parse syntax Syntax Err
  • Django 预关闭钩子关闭挂起的 pymongo 连接

    我在 Django 项目中使用 pymongo 最近我开始遇到一个问题 在退出主 Django 进程 即使通过管理命令 时 pymongo 连接将挂起 并且该进程永远不会退出 显然 堆栈中的某个地方出了问题 但目前最好的解决方案似乎是在 D
  • @UsePipes(ValidationPipe) 不适用于泛型(抽象控制器)

    我正在使用构建 APINest js和MySQL 由于敏捷性和 DRY 原则 我正在创建一个 OOP 结构 它为给定实体 来自 TypeORM 设置所有基本 CRUD 端点 主要目标是避免为不同的实体编写相同的通用方法 为了实现这一目标 我