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