我碰到一个触发POST端点的麻烦,该端点会触发我的postgres DB的typeorm repository.save()方法。
这是我的DTO对象:
import { ApiProperty } from '@nestjs/swagger/';
import { IsString, IsUUID} from 'class-validator';
import { Client } from '../../../models';
import { User } from '../../../user.decorator';
export class ClientDTO implements Readonly<ClientDTO> {
@ApiProperty({ required: true })
@IsUUID()
id: string;
@ApiProperty({ required: true })
@IsString()
name: string;
public static from(dto: Partial<ClientDTO>) {
const cl = new ClientDTO();
cl.id = dto.id;
cl.name = dto.name;
return cl;
}
public static fromEntity(entity: Client) {
return this.from({
id: entity.id,
name: entity.name,
});
}
public toEntity = (user: User | null) => {
const cl = new Client();
cl.id = this.id;
cl.name = this.name;
cl.createDateTime = new Date();
cl.createdBy = user ? user.id : null;
cl.lastChangedBy = user ? user.id : null;
return cl;
}
}
我在POST上的控制器-/client
:
import {
Body,
Controller,
Get, Post
} from '@nestjs/common';
import { ClientDTO } from './dto/client.dto';
import { ClientService } from './client.service';
import { User } from 'src/user.decorator';
@Controller('client')
export class ClientController {
constructor(
private clientService: ClientService
) { }
@Get()
public async getAllClients(): Promise<ClientDTO[]> {
return this.clientService.getAllClients();
}
@Post()
public async createClient(@User() user: User, @Body() dto: ClientDTO): Promise<ClientDTO> {
return this.clientService.createClient(dto, user);
}
}
我的服务:
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Client } from '../../models';
import { ClientDTO } from './dto/client.dto';
import { User } from '../../user.decorator';
@Injectable()
export class ClientService {
constructor(
@InjectRepository(Client) private readonly clientRepository: Repository<Client>
) {}
public async getAllClients(): Promise<ClientDTO[]> {
return await this.clientRepository.find()
.then(clients => clients.map(e => ClientDTO.fromEntity(e)));
}
public async createClient(dto: ClientDTO, user: User): Promise<ClientDTO> {
return this.clientRepository.save(dto.toEntity(user))
.then(e => ClientDTO.fromEntity(e));
}
}
我收到500个内部服务器错误,并显示日志消息,指出我的ClientDTO.toEntity不是函数。
TypeError: dto.toEntity is not a function
at ClientService.createClient (C:\...\nest-backend\dist\features\client\client.service.js:29:47)
at ClientController.createClient (C:\...\nest-backend\dist\features\client\client.controller.js:27:35)
at C:\...\nest-backend\node_modules\@nestjs\core\router\router-execution-context.js:37:29
at process._tickCallback (internal/process/next_tick.js:68:7)
我很困惑,因为这仅通过http请求发生。我有一个脚本,可以在一个名为seed.ts的Docker容器中重新启动它后,为我的开发数据库播种:
import * as _ from 'lodash';
import { Client } from '../models';
import { ClientDTO } from '../features/client/dto/client.dto';
import { ClientService } from '../features/client/client.service';
import { configService } from '../config/config.service';
import { createConnection, ConnectionOptions } from 'typeorm';
import { User } from '../user.decorator';
async function run() {
const seedUser: User = { id: 'seed-user' };
const seedId = Date.now()
.toString()
.split('')
.reverse()
.reduce((s, it, x) => (x > 3 ? s : (s += it)), '');
const opt = {
...configService.getTypeOrmConfig(),
debug: true
};
const connection = await createConnection(opt as ConnectionOptions);
const clientService = new ClientService(connection.getRepository(Client));
const work = _.range(1, 10).map(n => ClientDTO.from({
name: `seed${seedId}-${n}`,
}))
######################## my service calls ClientDTO.toEntity() without issue ###########################
.map(dto => clientService.createClient(dto, seedUser)
.then(r => (console.log('done ->', r.name), r)))
return await Promise.all(work);
}
run()
.then(_ => console.log('...wait for script to exit'))
.catch(error => console.error('seed error', error));
这让我觉得我缺少简单/明显的东西。
谢谢!
答案 0 :(得分:3)
看起来您正在使用 ValidationPipe。这里提到了解决方案 https://github.com/nestjs/nest/issues/552
在设置验证管道时,您需要告诉它进行例如转换
app.useGlobalPipes(new ValidationPipe({
transform: true
}));
答案 1 :(得分:1)
通过api调用进入时,您的dto并不是基于类的对象,而只是一个通用对象。因此,它不能具有方法,因此您的toEntity方法将不起作用。您收到的错误消息是红色提示,它没有告诉您失败的真正原因。
您可以通过以下方法解决此问题:根据您的类创建一个新对象,然后在该新对象上调用一个方法,以从普通对象dto复制属性,或者使用class-transformer库,或者通过任何您想要的东西可以达到相同的结果。
答案 2 :(得分:1)
在控制器中像这样dto
声明dto: ClientDTO
的事实不足以创建类的实例。这仅是对您和项目中其他开发人员的指示,以防止在应用程序的其余部分中滥用dto对象。
为了拥有类的实例并使用该类中的方法,必须显式设置如下映射:
@Post()
public async createClient(@User() user: User, @Body() dto: ClientDTO): Promise<ClientDTO> {
const client = ClientDTO.from(dto);
return this.clientService.createClient(client, user);
}
假设ClientDTO.from
是用于dto
中包含的数据的正确函数。如果没有,请对其进行调整,创建一个新的,或添加一个constructor
。