ウェブサイト検索

TypeScript - NestJSでclass-validatorを使ってバリデータ制約インターフェースにサービスを注入するにはどうすればよいですか?


私はバリデータ制約インターフェースにユーザーサービスを注入しようとしていますが、うまくいかないようです:

import { ValidatorConstraintInterface, ValidatorConstraint, ValidationArguments, registerDecorator, ValidationOptions } from "class-validator";
import { UsersService } from './users.service';

@ValidatorConstraint({ async: true })
export class IsEmailAlreadyInUseConstraint implements ValidatorConstraintInterface {
    constructor(private usersService: UsersService) {
        console.log(this.usersService);
    }
    validate(email: any, args: ValidationArguments) {
        return this.usersService.findUserByEmail(email).then(user => {
             if (user) return false;
             return true;
        });
        return false;
    }

}

しかし、usersService が null としてログに記録されているため、そのメソッドにアクセスできません。

この件について何か見解はありますか?

回答 #1

この問題で苦しんでいるかもしれない方々へ:

class-validator は、カスタムバリデータ制約クラスに依存関係を注入したい場合、サービスコンテナを使用する必要があります。出典: https://github.com/typestack/class-validator#using-service-container

import {useContainer, Validator} from "class-validator";

// do this somewhere in the global application level:
useContainer(Container);

そのため、ユーザーコンテナ機能をグローバルなアプリケーションレベルに追加する必要があります。

1. app 宣言の後、main.ts の bootstrap 関数に次のコードを追加します:

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  useContainer(app.select(AppModule), { fallbackOnErrors: true });
...}

{fallbackOnErrors: true} は必須です。なぜなら、DI に必要なクラスがない場合、Nest は例外を投げるからです。

2. 制約に Injectable() を追加してください:

import {ValidatorConstraint, ValidatorConstraintInterface} from 'class-validator';
import {UsersService} from './user.service';
import {Injectable} from '@nestjs/common';

@ValidatorConstraint({ name: 'isUserAlreadyExist', async: true })
@Injectable() // this is needed in order to the class be injected into the module
export class IsUserAlreadyExist implements ValidatorConstraintInterface {
    constructor(protected readonly usersService: UsersService) {}

    async validate(text: string) {
        const user = await this.usersService.findOne({
            email: text
        });
        return !user;
    }
}

3. 制約をモジュールにプロバイダーとして注入し、制約に注入する予定のサービスもモジュールレベルで利用可能であることを確認してください:

import {Module} from '@nestjs/common';
import { UsersController } from './user.controller';
import { UsersService } from './user.service';
import { IsUserAlreadyExist } from './user.validator';

@Module({
    controllers: [UsersController],
    providers: [IsUserAlreadyExist, UsersService],
    imports: [],
    exports: []
})
export class UserModule {
}

回答 #2

ところで、これはe2eテストでは動きません。これが私が動かす方法です。

beforeAll(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
        imports: [AppModule],
    }).compile();
    app = moduleFixture.createNestApplication();

    app.useGlobalPipes(GetValidationPipe());
    useContainer(app.select(AppModule), { fallbackOnErrors: true });
    await app.init();
});

関連記事