- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我一直很难让子实体使用 REST api 自动工作。
我有一个基类:
class Block {
@PrimaryGeneratedColumn('uuid')
public id: string;
@Column()
public type: string;
}
然后将其扩展到其他 block 类型,例如:
@Entity('sites_blocks_textblock')
class TextBlock extends Block {
@Column()
public text: string;
}
我让每个 block 类型都有自己的实体,以便列可以正确序列化到数据库,并对每个属性进行验证。
所以...我有 10 多种 block 类型,并且我试图避免使用单独的 Controller 和 CRUD 端点来每种 block 类型。我只想要一个 BlockController、一个/block 端点、POST 来创建,然后 PUT 到/block/:id 上进行更新,这样它就可以从请求的“type”主体参数推断出 block 的类型。
问题是,在请求中,最后一个 @Body() 参数将不会验证(请求不会通过),除非我使用类型“any”...因为每个自定义 block 类型都传递它的额外/自定义属性。否则,我将不得不使用每个特定的 Block 子类作为参数类型,需要为每种类型提供自定义方法。
为了实现这一目标,我尝试使用自定义验证管道和泛型,我可以在其中查看传入的“类型”主体参数,并将传入数据强制转换或实例化为特定的 block 类型。
Controller 处理程序:
@Post()
@UseGuards(PrincipalGuard)
public create(@Principal() principal: User,
@Param('siteId', ParseUUIDPipe) siteId: string,
@Body(new BlockValidationPipe()) blockCreate: any): Promise<Block> {
return this.blockService.create(principal.organization, siteId, blockCreate);
}
BlockValidationPipe(这应该将传入的数据对象转换为特定的 block 类型,然后验证它,以该类型返回传入的数据对象):
@Injectable()
export class BlockValidationPipe implements PipeTransform<any> {
async transform(value: any, { metatype }: ArgumentMetadata) {
if (value.type) {
if (value.type.id) {
metatype = getBlockTypeFromId(value.type.id);
}
}
if (!metatype || !this.toValidate(metatype)) {
return value;
}
// MAGIC: ==========>
let object = objectToBlockByType(value, value.type.id, metatype);
const errors = await validate(object);
if (errors.length > 0) {
throw new BadRequestException(errors, 'Validation failed');
}
return object ? object : value;
}
private toValidate(metatype: Function): boolean {
const types: Function[] = [String, Boolean, Number, Array, Object];
return !types.includes(metatype);
}
}
使用这个助手(但它可能无法完全按照预期工作,还没有完全传递类型):
function castOrNull<C extends Block>(value: C, type): C | null {
return value as typeof type;
}
export function objectToBlockByType(object, typeId, metatype) {
switch(typeId) {
case 'text':
return castOrNull<TextBlock>(object, TextBlock);
case 'avatar':
return castOrNull<AvatarBlock>(object, AvatarBlock);
case 'button':
return castOrNull<ButtonBlock>(object, ButtonBlock);
// etc....
default:
return castOrNull<Block>(object, Block);
}
}
...这一切都应该为我提供一个适当的 block 子类实例化以供 Controller 使用,但我不确定如何将此特定子类类型传递给底层服务调用以更新每个 block 的特定 block 存储库实体类型。使用泛型可以做到这一点吗?
例如,在BlockService中,但我应该将特定的 block 类型(TextBlock、ButtonBlock等)传递给repository.save()方法,以便它将子类类型正确序列化到各自的表中。我假设这是可以做到的,但是如果我错了,请有人纠正我......
我正在尝试这样做,我将 block 数据作为其 block 父类型传递,然后尝试获取其特定的类类型来传递以保存,但它不起作用......
public async create(organization: Organization, siteId: string, blockCreate: Block): Promise<Block> {
let blockType: Type<any> = getBlockTypeFromId(blockCreate.type.id);
console.log("create block", typeof blockCreate, blockCreate.constructor.name, blockCreate, typeof blockType, blockType);
///
let r = await this.blockRepository.save<typeof blockCreate>({
organization: organization,
site: await this.siteService.getByIdAndOrganization(siteId, organization),
type: await this.blockTypeService.getById(blockCreate.type.id),
...blockCreate
});
//r.data = JSON.parse(r.data);
return r;
}
这里的问题是“typeof blockCreate”总是返回“object”,我必须调用“blockCreate.constructor.name”来获取正确的子类 block 类型名称,但无法将其作为类型 T 传递。
所以我想知道......是否有办法将子类类型 T 参数从 Controller 助手(应该在其中转换和验证子类型)一路返回到存储库,以便我可以传递此类型 T到 save(entity) 调用...并且正确提交了吗?或者,如果“typeof block”不返回特定的子类类型,是否有其他方法可以从对象实例本身获取此类型 T?我认为不可能在编译时执行前者...?
我真的只是想让子类序列化和验证与一组 Controller 端点和服务层/存储库调用一起工作......我应该研究部分实体吗?
有人知道我可以寻找什么方向来实现这一目标吗?
最佳答案
让我们简单地设置两个基类/泛型类:
每个都具有以下通用类型:
然后,您只需为您的特定实体扩展它们并提供实体即可。
请注意,本质上,整个事情只是围绕 TypeOrm 提供的功能的几个通用包装类。
这是这个想法的骨架,但我测试了它们,它们对我来说效果很好。 (代码带有注释)。
让我们从具有一些常见 db/REST 功能的通用服务类开始:
import { Repository, DeepPartial, SaveOptions } from "typeorm";
import { Injectable } from '@nestjs/common';
/**
* Provides common/general functionality for working with db data
* via TypeOrm API.
*
* see:
* https://github.com/typeorm/typeorm/blob/master/docs/repository-api.md
*
* You can extend this service class for functionalities specific
* to your given Entity.
*
* A service is the work-horse for handling the tasks
* (such as fetching the data from data source / db)
* delegated by a controller.
* The service is injected in the controller who delegates the tasks
* to the service for specific data sets / Entities / db tables.
*/
@Injectable()
export class DbGenService<E> {
/**
* @param repo
* is TypeOrm repository for your given Entity <E>.
* (the intermediary object, which does all the work on the db end).
*/
constructor(readonly repo: Repository<E>) {}
/**
* (AUX function to create entity object):
* Creates a new entity/entities and copies all entity properties
* from given objects into their new entities.
* Note that it copies only properties that are present in the entity schema.
* @param obj
*/
async createE(obj): Promise<E[]> {
return this.repo.create(obj);
}
/**
* (AUX function to merge two entity objects, 1st can be set empty):
* Merges multiple entities (or entity-like objects) into a given entity.
*
* @param mergeIntoEntity
* the initial / source and
* finally the target/resulting/merged entity
* Can be initilized with an empty object e.g:
* let e: E = {} as E;
* @param entityLikes
* partial entity or an object looking like the entity
*/
async mergeEs(mergeIntoEntity: E, ...entityLikes: DeepPartial<E>[]): Promise<E> {
return this.repo.merge(mergeIntoEntity, ...entityLikes);
}
/**
* Saves a given entity in the database.
* If entity does not exist in the database,
* then inserts, otherwise updates.
*/
async saveRecord(recordEntity: E): Promise<E> {
return await this.repo.save(recordEntity);
}
/**
* Saves all given entities (array) in the database.
* If entities do not exist in the database,
* then inserts, otherwise updates.
*/
async saveRecords<T extends DeepPartial<E>>(entities: T[], options?: SaveOptions): Promise<(T & E)[]> {
return await this.repo.save(entities, options);
}
/**
* Return all the records of the db table for this Entity
*/
async getAllRecords(): Promise<E[]> {
return await this.repo.find();
}
/**
* Return the record of the db table for this Entity
* having
* @param id = id
*/
async getRecordById(recID: number): Promise<E> {
return await this.repo.findOne(recID);
}
/**
* Deletes the records of the db table for this Entity
* having query statement:
* @param query = query
*/
async deleteAllRecords(): Promise<void> {
await this.repo.clear();
}
/**
* deletes the record of the db table for this Entity
* having
* @param id = id
*/
async deleteRecord(id): Promise<void> {
await this.repo.delete(id);
}
// ... + add your common db functions here
// and match them with the generic controller ....
}
接下来,您编写一个通用 Controller ,它将工作负载委托(delegate)给服务 - 匹配服务功能 - 如下所示:
import { DeepPartial } from 'typeorm';
import { Controller, Get, Query, Post, Body, Put, Param, Delete } from '@nestjs/common';
import { DbGenService } from './db-gen.service';
/**
* General/base controller - handles basic HTTP requests of:
* Get, Query, Post, Body, Put, Param, Delete.
*
* Provides general/base/shared db functionality
* (layed out in the service class: DbGenService<E> - via TypeOrm API)
* to exteded controllers of this DbGenController class.
*
* You can use this controller as a base class for your
* specific controllers that share the same functionalities
* with this controller.
*
* Simply extend it like this:
*
* @Controller('myRoute')
* export class MyController extends DbGenController<MyEntity> { ... }
*
* the extended router than handles requests such as
* e.g:
* http://localhost:3000/myRoute
* http://localhost:3000/myRoute/1
*
*
*/
@Controller()
export class DbGenController<E> {
/**
* DbGenService is the class with the generic working functions
* behind the controller
*/
constructor(private dbGenService: DbGenService<E>) {}
/**
* Saves all given entities (array) in the database.
* If entities do not exist in the database,
* then inserts, otherwise updates.
*/
@Post()
async saveRecord(@Body() dto: DeepPartial<E>) {
// create the Entity from the DTO
let e: E[] = await this.dbGenService.createE(dto);
// OR:
// let e: E = {} as E;
// e = await this.dbGenService.mergeEs(e, dto);
const records = await this.dbGenService.saveRecords(e);
return records;
}
/**
* Return all the records of the db table for this Entity
*/
@Get()
async getAllRecords(): Promise<E[]> {
const records = await this.dbGenService.getAllRecords();
return records;
}
/**
* Return the record of the db table for this Entity
* having
* @param id = id
*/
@Get(':id')
async getRecordById(@Param('id') id): Promise<E> {
const records = await this.dbGenService.getRecordById(id);
return records;
}
/**
* Return the record of the db table for this Entity
* having
* @param id = id
*/
@Get()
async getRecordByFVs(@Param('id') id): Promise<E> {
const records = await this.dbGenService.getRecordById(id);
return records;
}
/**
* Deletes all the records of the db table for this Entity
*/
@Delete()
async deleteAllRecords(): Promise<void> {
const records = await this.dbGenService.deleteAllRecords();
return records;
}
/**
* Deletes the records of the db table for this Entity
* having query statement:
* @param query = query
*/
@Delete()
async deleteRecord(@Query() query): Promise<void> {
const records = await this.dbGenService.deleteRecord(query.ID);
return records;
}
/**
* Deletes the record of the db table for this Entity
* having
* @param id = id
*/
@Delete(':id')
deleteRecordById(@Param('id') id): Promise<void> {
return this.dbGenService.deleteRecord(id);
}
}
...现在是美丽/有趣的部分 - 将它们用于您想要的任何实体 - 例如 UsersEntity - 服务:
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { Injectable } from '@nestjs/common';
import { DbGenService } from '../../generic/db-gen.service';
import { UsersEntity } from '../../../entities/users.entity';
/**
* Users db records service.
*
* General doc:
* ------------
* A db service is the work-horse for handling the tasks
* (such as fetching the data from data source / db)
* delegated by a controller.
* The service is injected in the controller.
*
* This service extends the usege of the common/generic
* db taks/functions of the service class: DbGenService<E>,
* where <E> is the given Entity type, which we we pass to the
* DbGenService instance, reflecting so exactly the Entity
* of this extended class - in this case the: UsersEntity
*/
@Injectable()
export class UsersService<UsersEntity> extends DbGenService<UsersEntity> {
constructor(@InjectRepository(UsersEntity) repo: Repository<UsersEntity>) {
super(repo);
}
}
现在 UsersEntity - Controller :
import { Controller } from '@nestjs/common';
import { appCfg } from '../../../../config/app-config.service';
import { DbGenController } from '../../generic/db-gen.controller';
import { UsersEntity } from '../../../entities/users.entity';
import { UsersService } from './users.service';
/**
* Controller - handles HTTP requests.
*
* This controller handles routes of HTTP requests with suffix:
* /users
* due to the decorator:
* @Controller('users')
* e.g:
* http://localhost:3000/users
* http://localhost:3000/users/1
*
* This service extends the usage of the common/generic
* db controller class: DbGenController<E>,
* where <E> is the given Entity type, which we we pass to the
* DbGenController instance, reflecting so exactly the Entity
* of this extended class - in this case the: UsersEntity
*/
@Controller('users')
export class UsersController extends DbGenController<UsersEntity> {
constructor(private usersService: UsersService<UsersEntity>) {
super(usersService);
}
}
...当然,将其链接在一起:
import { Module } from '@nestjs/common';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
import { UsersEntity } from '../../../entities/users.entity';
import { TypeOrmModule } from '@nestjs/typeorm';
/**
* UsersModule is used to export the UsersService,
* so that other modules, specifically the AuthModule,
* can communicate with the database to perform
* its user authentication functions via an access to UsersService.
*/
@Module({
imports: [TypeOrmModule.forFeature([UsersEntity])],
controllers: [UsersController],
providers: [UsersService]
})
export class UsersModule {}
与“UsersEntity”类似,您现在可以将通用服务和通用 Controller 中放置的所有上述 REST 功能应用到任何其他实体,而无需在其 Controller 或服务内重写任何功能。而且,您仍然可以灵活地将特定的 REST/数据库功能应用于各个扩展类内的每个实体 Controller /服务。
现在,请记住,这只是一个基本的框架设计,需要所有其他必需品,但应该让您开始使用这种方法,这可能适合某些人,也可能不适合某些人。
REST 示例的一些语法直接来自 NestJs 文档/网站。
(TS大师们请随时提供改进、建议等,特别是在装饰器方面,我很幸运在这里经历过......)
关于typescript - TypeORM+nestjs : How to serialize+CRUD child entity classes using parent generics, 具有单个 Controller 、存储库、子类型验证?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60443112/
关闭。这个问题是opinion-based 。目前不接受答案。 想要改进这个问题吗?更新问题,以便 editing this post 可以用事实和引文来回答它。 . 已关闭 9 年前。 Improv
我刚刚发现了这门语言,我想知道是否可以使用数据库制作基本的 CRUD 网络应用程序。 最佳答案 有 mysql 的库和 postgresql , 核心库提供了一个 web server支持 HTTP、
Symfony 4.0 发布后,不再支持 SensioGeneratorBundle .因此命令 php app/console generate:doctrine:crud不可用。 他们建议使用 M
在开发 Web 应用程序时,我通常会看到人们执行增删改查和同步 View 的两种方式。 这里是使用 ajax 的高级示例。 1-方法一 创建操作可能涉及 POST 请求,成功后只需执行另一个 GET
我已经成功地使用 Yii2 模型和 CRUD 生成器为我的网络应用程序获取了一些框架代码文件。特别是,CRUD Generator 声称已成功将其 View 文件创建到: /basic/views//
在我的项目中,我一直在使用 Django 的通用 CRUD View 来处理很多事情。我现在想开始迁移到 DJango 1.3 中基于类的新样式通用 CRUD View 。我没有发现这些文档有多大帮助
关闭。这个问题需要details or clarity .它目前不接受答案。 想改进这个问题?通过 editing this post 添加详细信息并澄清问题. 7年前关闭。 Improve this
我希望标题不要太含糊,所以这里是: 我创建了一个 MySQL 数据库,其中存储了两个表:一个包含输入数据,另一个包含输出数据。 之后我编写了一个程序,连接到这个特定的数据库,从输入表中提取数据,解析它
我需要编辑我的实体以获得更多值。我已经用我之前的值生成了 crud。如何在编辑实体后通过应用程序/控制台重新生成 crud,以便它自动为其他值生成函数。 最佳答案 为此,您需要删除为此 crud 生成
因此,我仅使用 JavaScript 创建了一个简单的 CRUD 应用程序。现在,您可以将国家/地区添加到数组中并从数组中删除国家/地区。我希望能够编辑数组中的现有国家/地区,例如我想将“斯德哥尔摩”
我想让java中的一个类有一个可以与Hibernate配置交互并执行某些操作的方法,该方法被标识为ENUM(例如:读取,更新,添加,删除等) 方法参数应为(枚举操作、类 DTO、NamedQuery
我正在构建一个 React 应用程序,并使用 auth0 来登录/验证用户。 在使用 auth0 之前,我一直在对 API 进行 CRUD 调用来发布帖子。这又是在使用 auth0 之前、在我拥有用户
尝试使用 BlueJ 构建我的第一个 Java MySQL CRUD 应用程序。我可以运行该应用程序并将数据写入 MySQL 数据库。但是,当我运行搜索函数时,我得到了 Java .NullPoint
我正在试用 Microsoft Master Data Services,我想以编程方式将数据添加到数据库中。我开始获得模型/实体/成员结构,但我还不确定。如果您对此结构有很好的解释,请分享。 假设有
我正在尝试开发一个 Backbone Marionette 应用程序,我需要知道如何以最佳方式执行 CRUD(创建、读取、更新和销毁)操作。我找不到任何解释这一点的资源(仅适用于 Backbone)。
我已经根据文档和 medium article 模拟了与 Room 的多对多关系。 .使用这个@Relation,我可以从数据库中检索RecipeWithIngredients 或Ingredient
Closed. This question is opinion-based。它当前不接受答案。 想要改善这个问题吗?更新问题,以便editing this post用事实和引用来回答。 3年前关闭。
通过AngularJS通过REST进行CRUD操作的最佳实践是什么? 特别是这里的 Angular-Way 。我的意思是使用最少代码和最默认 Angular 设置来达到此目的的方式。 我知道$ res
我无法弄清楚我的更新功能。我能够从数据库中检索和删除,但我不知道如何更新。这对我来说是全新的,所以我很困惑。 .js 文件 //update user $("#btnUpdateUser").clic
我正在寻找一种对用户透明的 CRUD 操作后重新加载页面的方法。 实际上,在创建或删除之后,我必须重新加载页面才能显示我的操作。 我使用 api 来实现这个,当我将它与 json 文件一起使用时,它工
我是一名优秀的程序员,十分优秀!