Как обработать уникальную ошибку проверки поля сущности typeorm в nestjs?

Я установил собственный уникальный декоратор валидатора в своем электронном письме с полем сущности typeORM. В Nestjs есть внедрение зависимостей, но сервис не внедряется.

Ошибка: TypeError: невозможно прочитать свойство findByEmail из undefined.

Любая помощь по реализации настраиваемого валидатора электронной почты?

user.entity.ts

@Column()
@Validate(CustomEmail, {
    message: "Title is too short or long!"
})
@IsEmail()
email: string;

Мой валидатор CustomEmail

import {ValidatorConstraint, ValidatorConstraintInterface, 
ValidationArguments} from "class-validator";
import {UserService} from "./user.service";

@ValidatorConstraint({ name: "customText", async: true })
export class CustomEmail implements ValidatorConstraintInterface {

  constructor(private userService: UserService) {}
  async validate(text: string, args: ValidationArguments) {

    const user = await this.userService.findByEmail(text);
    return !user; 
  }

  defaultMessage(args: ValidationArguments) { 
    return "Text ($value) is too short or too long!";
  }
}

Я знаю, что могу установить уникальные параметры столбца

@Column({
  unique: true
})

но это вызывает ошибку mysql и ExceptionsHandler, который приводит к сбою моего приложения, поэтому я не могу справиться с этим сам ...

Спасибо!


person LuJaks    schedule 18.02.2018    source источник
comment
Вы не можете внедрять зависимости внутри класса ValidatorConstraint. Это не часть контейнера Nest.   -  person Kamil Myśliwiec    schedule 24.02.2018


Ответы (2)


Здесь я могу предложить 2 разных подхода: первый ловит ошибку нарушения ограничения локально без дополнительного запроса, а второй использует глобальный фильтр ошибок, вылавливая такие ошибки во всем приложении. Я лично пользуюсь последним.

Решение для локального запроса no-db

Нет необходимости делать дополнительный запрос к базе данных. Вы можете поймать ошибку, нарушающую уникальное ограничение, и передать клиенту любой HttpException, который хотите. В users.service.ts:

  public create(newUser: Partial<UserEntity>): Promise<UserEntity> {
    return this.usersRepository.save(newUser).catch((e) => {
      if (/(email)[\s\S]+(already exists)/.test(e.detail)) {
        throw new BadRequestException(
          'Account with this email already exists.',
        );
      }
      return e;
    });
  }

Что вернет:

Скриншот ошибки из Insomnia (приложение для MacOS)

Решение для глобального фильтра ошибок

Или даже создайте глобальный QueryErrorFilter:

@Catch(QueryFailedError)
export class QueryErrorFilter extends BaseExceptionFilter {
  public catch(exception: any, host: ArgumentsHost): any {
    const detail = exception.detail;
    if (typeof detail === 'string' && detail.includes('already exists')) {
      const messageStart = exception.table.split('_').join(' ') + ' with';
      throw new BadRequestException(
        exception.detail.replace('Key', messageStart),
      );
    }
    return super.catch(exception, host);
  }
}

Затем в main.ts:

async function bootstrap() {
  const app = await NestFactory.create(/**/);
  /* ... */
  const { httpAdapter } = app.get(HttpAdapterHost);
  app.useGlobalFilters(new QueryErrorFilter(httpAdapter));
  /* ... */
  await app.listen(3000);
}
bootstrap();

Это даст общее сообщение об ошибке $table entity with ($field)=($value) already exists.. Пример:

введите описание изображения здесь

person Daniel Kucal    schedule 07.03.2021
comment
Похоже, имхо хорошее решение, особенно глобальный фильтр. Получил, что он работает с использованием exception = new BadRequestException (...) вместо throw, его немедленное выбрасывание привело к потере всех исключений, возвращаемых клиенту. - person johnkork; 29.06.2021

Я изменил свой код. Я проверяю уникальность имени пользователя / электронной почты в пользовательской службе (вместо настраиваемого валидатора) и возвращаю HttpExcetion, если пользователь уже вставлен в БД.

person LuJaks    schedule 25.02.2018