使用 `class-validator` 在 TypeScript 中确认密码
Password confirmation in TypeScript with `class-validator`
今天,我想弄清楚如何在应用程序的后端 (NestJS) 验证注册表单。我只是想知道是否存在一种方法来验证 password
和 passwordConfirm
匹配,使用 class-validator
包来构建自定义验证器或利用提供的验证器。我正在考虑 class 验证器,而不是现场验证器。
// Maybe validator here
export class SignUpDto {
@IsString()
@MinLength(4)
@MaxLength(20)
username: string;
@IsString()
@MinLength(4)
@MaxLength(20)
@Matches(/((?=.*\d)|(?=.*\W+))(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*$/, {message: 'password too weak'})
password: string;
@IsString()
@MinLength(4)
@MaxLength(20)
passwordConfirm: string;
}
你有什么建议?
终于在我问题的评论中@ChristopheGeers的建议下解决了密码匹配问题:
@piero: It's not supported yet as mentioned. But here's an example decorator (@IsLongerThan): LINK .... it checks if a property is longer than another one. So it's possible to compare one property against another. You can use this example to create a decorator that does what you want.
这是我提出的解决方案:
符号-up.dto.ts
export class SignUpDto {
@IsString()
@MinLength(4)
@MaxLength(20)
username: string;
@IsString()
@MinLength(4)
@MaxLength(20)
@Matches(/((?=.*\d)|(?=.*\W+))(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*$/, {message: 'password too weak'})
password: string;
@IsString()
@MinLength(4)
@MaxLength(20)
@Match('password')
passwordConfirm: string;
}
match.decorator.ts
import {registerDecorator, ValidationArguments, ValidationOptions, ValidatorConstraint, ValidatorConstraintInterface} from 'class-validator';
export function Match(property: string, validationOptions?: ValidationOptions) {
return (object: any, propertyName: string) => {
registerDecorator({
target: object.constructor,
propertyName,
options: validationOptions,
constraints: [property],
validator: MatchConstraint,
});
};
}
@ValidatorConstraint({name: 'Match'})
export class MatchConstraint implements ValidatorConstraintInterface {
validate(value: any, args: ValidationArguments) {
const [relatedPropertyName] = args.constraints;
const relatedValue = (args.object as any)[relatedPropertyName];
return value === relatedValue;
}
}
这是一个扩展示例,它内联验证器并为其提供默认消息。这样您就不必在每次使用 @IsEqualTo
装饰器时都输入消息。
import {
registerDecorator,
ValidationArguments,
ValidationOptions
} from 'class-validator';
export function IsEqualTo(property: string, validationOptions?: ValidationOptions) {
return (object: any, propertyName: string) => {
registerDecorator({
name: 'isEqualTo',
target: object.constructor,
propertyName,
constraints: [property],
options: validationOptions,
validator: {
validate(value: any, args: ValidationArguments) {
const [relatedPropertyName] = args.constraints;
const relatedValue = (args.object as any)[relatedPropertyName];
return value === relatedValue;
},
defaultMessage(args: ValidationArguments) {
const [relatedPropertyName] = args.constraints;
return `${propertyName} must match ${relatedPropertyName} exactly`;
},
},
});
};
}
@MinLength(requiredlength ex: 5)
@MaxLength(requiredlength ex:5)
正在使用最新版本,因此我们可以使用它来验证长度。
接受的答案对我来说非常好,但我们可能会犯拼写错误,例如:
@Match('passwordd')
//
所以我想让 Generics
更严格
@Match(SignUpDto, (s) => s.password)
match.decorator.ts
import { ClassConstructor } from "class-transformer";
export const Match = <T>(
type: ClassConstructor<T>,
property: (o: T) => any,
validationOptions?: ValidationOptions,
) => {
return (object: any, propertyName: string) => {
registerDecorator({
target: object.constructor,
propertyName,
options: validationOptions,
constraints: [property],
validator: MatchConstraint,
});
};
};
@ValidatorConstraint({ name: "Match" })
export class MatchConstraint implements ValidatorConstraintInterface {
validate(value: any, args: ValidationArguments) {
const [fn] = args.constraints;
return fn(args.object) === value;
}
defaultMessage(args: ValidationArguments) {
const [constraintProperty]: (() => any)[] = args.constraints;
return `${constraintProperty} and ${args.property} does not match`;
}
}
所以我们可以像这样使用 Match
装饰器:
export class SignUpDto {
// ...
password: string;
// finally, we have
@Match(SignUpDto, (s) => s.password)
passwordConfirm: string;
}
我喜欢 IsEqualTo 装饰器的方法,但我也担心使用不在我的 Dto 中的 属性 时的拼写错误,所以我最终得到:
import {
registerDecorator,
ValidationArguments,
ValidationOptions,
} from 'class-validator';
export function IsEqualTo<T>(
property: keyof T,
validationOptions?: ValidationOptions,
) {
return (object: any, propertyName: string) => {
registerDecorator({
name: 'isEqualTo',
target: object.constructor,
propertyName,
constraints: [property],
options: validationOptions,
validator: {
validate(value: any, args: ValidationArguments) {
const [relatedPropertyName] = args.constraints;
const relatedValue = (args.object as any)[relatedPropertyName];
return value === relatedValue;
},
defaultMessage(args: ValidationArguments) {
const [relatedPropertyName] = args.constraints;
return `${propertyName} must match ${relatedPropertyName} exactly`;
},
},
});
};
}
并像这样使用它:
export class CreateUserDto {
@IsEqualTo<CreateUserDto>('password')
readonly password_confirmation: string;
}
我喜欢接受的答案,但我认为我们可以通过将要验证的 属性 作为约束数组中的字符串传递给来简化过程。
示例:
@ValidatorConstraint({ name: 'CustomMatchPasswords', async: false })
export class CustomMatchPasswords implements ValidatorConstraintInterface {
validate(password: string, args: ValidationArguments) {
if (password !== (args.object as any)[args.constraints[0]]) return false;
return true;
}
defaultMessage(args: ValidationArguments) {
return "Passwords do not match!";
}
}
然后我们可以使用验证器而不需要创建装饰器:
@IsString()
@MinLength(4)
@MaxLength(20)
@Matches(/((?=.*\d)|(?=.*\W+))(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*$/, {message: 'password too weak'})
password: string;
@IsString()
@MinLength(4)
@MaxLength(20)
@Validate(CustomMatchPasswords, ['password'])
passwordConfirm: string;
今天,我想弄清楚如何在应用程序的后端 (NestJS) 验证注册表单。我只是想知道是否存在一种方法来验证 password
和 passwordConfirm
匹配,使用 class-validator
包来构建自定义验证器或利用提供的验证器。我正在考虑 class 验证器,而不是现场验证器。
// Maybe validator here
export class SignUpDto {
@IsString()
@MinLength(4)
@MaxLength(20)
username: string;
@IsString()
@MinLength(4)
@MaxLength(20)
@Matches(/((?=.*\d)|(?=.*\W+))(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*$/, {message: 'password too weak'})
password: string;
@IsString()
@MinLength(4)
@MaxLength(20)
passwordConfirm: string;
}
你有什么建议?
终于在我问题的评论中@ChristopheGeers的建议下解决了密码匹配问题:
@piero: It's not supported yet as mentioned. But here's an example decorator (@IsLongerThan): LINK .... it checks if a property is longer than another one. So it's possible to compare one property against another. You can use this example to create a decorator that does what you want.
这是我提出的解决方案:
符号-up.dto.ts
export class SignUpDto {
@IsString()
@MinLength(4)
@MaxLength(20)
username: string;
@IsString()
@MinLength(4)
@MaxLength(20)
@Matches(/((?=.*\d)|(?=.*\W+))(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*$/, {message: 'password too weak'})
password: string;
@IsString()
@MinLength(4)
@MaxLength(20)
@Match('password')
passwordConfirm: string;
}
match.decorator.ts
import {registerDecorator, ValidationArguments, ValidationOptions, ValidatorConstraint, ValidatorConstraintInterface} from 'class-validator';
export function Match(property: string, validationOptions?: ValidationOptions) {
return (object: any, propertyName: string) => {
registerDecorator({
target: object.constructor,
propertyName,
options: validationOptions,
constraints: [property],
validator: MatchConstraint,
});
};
}
@ValidatorConstraint({name: 'Match'})
export class MatchConstraint implements ValidatorConstraintInterface {
validate(value: any, args: ValidationArguments) {
const [relatedPropertyName] = args.constraints;
const relatedValue = (args.object as any)[relatedPropertyName];
return value === relatedValue;
}
}
这是一个扩展示例,它内联验证器并为其提供默认消息。这样您就不必在每次使用 @IsEqualTo
装饰器时都输入消息。
import {
registerDecorator,
ValidationArguments,
ValidationOptions
} from 'class-validator';
export function IsEqualTo(property: string, validationOptions?: ValidationOptions) {
return (object: any, propertyName: string) => {
registerDecorator({
name: 'isEqualTo',
target: object.constructor,
propertyName,
constraints: [property],
options: validationOptions,
validator: {
validate(value: any, args: ValidationArguments) {
const [relatedPropertyName] = args.constraints;
const relatedValue = (args.object as any)[relatedPropertyName];
return value === relatedValue;
},
defaultMessage(args: ValidationArguments) {
const [relatedPropertyName] = args.constraints;
return `${propertyName} must match ${relatedPropertyName} exactly`;
},
},
});
};
}
@MinLength(requiredlength ex: 5)
@MaxLength(requiredlength ex:5)
正在使用最新版本,因此我们可以使用它来验证长度。
接受的答案对我来说非常好,但我们可能会犯拼写错误,例如:
@Match('passwordd')
//
所以我想让 Generics
@Match(SignUpDto, (s) => s.password)
match.decorator.ts
import { ClassConstructor } from "class-transformer";
export const Match = <T>(
type: ClassConstructor<T>,
property: (o: T) => any,
validationOptions?: ValidationOptions,
) => {
return (object: any, propertyName: string) => {
registerDecorator({
target: object.constructor,
propertyName,
options: validationOptions,
constraints: [property],
validator: MatchConstraint,
});
};
};
@ValidatorConstraint({ name: "Match" })
export class MatchConstraint implements ValidatorConstraintInterface {
validate(value: any, args: ValidationArguments) {
const [fn] = args.constraints;
return fn(args.object) === value;
}
defaultMessage(args: ValidationArguments) {
const [constraintProperty]: (() => any)[] = args.constraints;
return `${constraintProperty} and ${args.property} does not match`;
}
}
所以我们可以像这样使用 Match
装饰器:
export class SignUpDto {
// ...
password: string;
// finally, we have
@Match(SignUpDto, (s) => s.password)
passwordConfirm: string;
}
我喜欢 IsEqualTo 装饰器的方法,但我也担心使用不在我的 Dto 中的 属性 时的拼写错误,所以我最终得到:
import {
registerDecorator,
ValidationArguments,
ValidationOptions,
} from 'class-validator';
export function IsEqualTo<T>(
property: keyof T,
validationOptions?: ValidationOptions,
) {
return (object: any, propertyName: string) => {
registerDecorator({
name: 'isEqualTo',
target: object.constructor,
propertyName,
constraints: [property],
options: validationOptions,
validator: {
validate(value: any, args: ValidationArguments) {
const [relatedPropertyName] = args.constraints;
const relatedValue = (args.object as any)[relatedPropertyName];
return value === relatedValue;
},
defaultMessage(args: ValidationArguments) {
const [relatedPropertyName] = args.constraints;
return `${propertyName} must match ${relatedPropertyName} exactly`;
},
},
});
};
}
并像这样使用它:
export class CreateUserDto {
@IsEqualTo<CreateUserDto>('password')
readonly password_confirmation: string;
}
我喜欢接受的答案,但我认为我们可以通过将要验证的 属性 作为约束数组中的字符串传递给来简化过程。
示例:
@ValidatorConstraint({ name: 'CustomMatchPasswords', async: false })
export class CustomMatchPasswords implements ValidatorConstraintInterface {
validate(password: string, args: ValidationArguments) {
if (password !== (args.object as any)[args.constraints[0]]) return false;
return true;
}
defaultMessage(args: ValidationArguments) {
return "Passwords do not match!";
}
}
然后我们可以使用验证器而不需要创建装饰器:
@IsString()
@MinLength(4)
@MaxLength(20)
@Matches(/((?=.*\d)|(?=.*\W+))(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*$/, {message: 'password too weak'})
password: string;
@IsString()
@MinLength(4)
@MaxLength(20)
@Validate(CustomMatchPasswords, ['password'])
passwordConfirm: string;