如何处理nestjs中的typeorm实体字段唯一验证错误?

LuJ*_*aks 3 typeorm nestjs

我在 typeORM 实体字段电子邮件上设置了一个自定义的唯一验证器装饰器。Nestjs有依赖注入,但是没有注入服务。

错误是:TypeError:无法读取未定义的属性“findByEmail”

对实现自定义电子邮件验证器有什么帮助吗?

用户实体.ts

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

我的 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!";
  }
}
Run Code Online (Sandbox Code Playgroud)

我知道我可以在列选项中设置唯一

@Column({
  unique: true
})
Run Code Online (Sandbox Code Playgroud)

但这会引发一个 mysql 错误和使我的应用程序崩溃的 ExceptionsHandler,所以我无法自己处理它...

谢谢!

LuJ*_*aks 11

我修改了我的代码。我正在检查用户服务中用户名/电子邮件的唯一性(而不是自定义验证器),并在用户已插入数据库的情况下返回 HttpExcetion。


Dan*_*cal 6

I can propose 2 different approaches here, the first one catches the constraint violation error locally without additional request, and the second one uses a global error filter, catching such errors in the entire application. I personally use the latter.

Local no-db request solution

No need to make additional database request. You can catch the error violating the unique constraint and throw any HttpException you want to the client. In 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;
    });
  }
Run Code Online (Sandbox Code Playgroud)

Which will return:

Insomnia (MacOS App) 错误截图

Global error filter solution

Or even create a global 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);
  }
}
Run Code Online (Sandbox Code Playgroud)

Then in 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();
Run Code Online (Sandbox Code Playgroud)

This will give generic $table entity with ($field)=($value) already exists. error message. Example:

在此处输入图片说明