Nest js POST-запрос не распознает метод DTO - PullRequest
0 голосов
/ 20 марта 2020

У меня возникли проблемы с попаданием в конечную точку POST, которая вызывает метод typeorm repository.save () к моей postgres БД.

Вот мой объект 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-запрос. У меня есть скрипт, который заполняет мою базу данных dev после того, как я запустил ее fre sh в docker контейнере с именем seed.ts:

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));

. Это заставляет меня думать, что я упускаю что-то простое / очевидное.

Спасибо!

Ответы [ 2 ]

1 голос
/ 21 марта 2020

Тот факт, что 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.

1 голос
/ 21 марта 2020

Ваш dto не был объектом на основе классов при входе через ваш вызов API - это просто обобщенный c объект. Поэтому у него не может быть методов, и поэтому ваш метод toEntity не будет работать. Полученное сообщение об ошибке - красная сельдь, которая не сообщает вам истинную причину сбоя.

Это можно исправить, создав новый объект на основе вашего класса, а затем вызвав метод для нового объекта, чтобы скопировать свойства из простого объекта dto, или с помощью библиотеки преобразователя классов, или с помощью все, что вы хотите, достигает того же результата.

...