前言

本项目使用的数据验证工具并非midwayjs官方提供的,而是使用class-validator。我也是刚接触这个工具不久,所以这篇文章算是我对这个工具整理的一些用法和心得。

class-validator官方文档提供了很多验证器,并且在文档底部将所有的验证器都列举出来了。如果觉得看官方文档入门比较困难的话,可以先看这篇帖子。简单学会如何使用后,可以根据需要在文档底部的验证器表格中找到自己想要的验证器,如果不满足需求,可以自己定制验证器

自定义验证器

  1. import { registerDecorator, ValidationOptions, ValidationArguments } from 'class-validator';
  2. //对比所在对象的指定属性的长度
  3. export function IsLongerThan(property: string, validationOptions?: ValidationOptions) {
  4. return function (object: Object, propertyName: string) {
  5. registerDecorator({
  6. name: 'isLongerThan',//验证器名称
  7. target: object.constructor,//目标
  8. propertyName: propertyName,//属性名
  9. constraints: [property],//约束条件
  10. options: validationOptions,//验证器执行参数
  11. validator: {
  12. validate(value: any, args: ValidationArguments) {
  13. const [relatedPropertyName] = args.constraints;
  14. const relatedValue = (args.object as any)[relatedPropertyName];
  15. return typeof value === 'string' && typeof relatedValue === 'string' && value.length > relatedValue.length; // you can return a Promise<boolean> here as well, if you want to make async validation
  16. },
  17. },
  18. });
  19. };
  20. }
  21. class MyClass {
  22. //将会拿name值和label值的长度进行比较
  23. @IsLongerThan('label', { message: '$property长度小于$constraint1' })
  24. name: string;
  25. label: string;
  26. }
  27. const model = new MyClass();
  28. model.name = 'hello world';
  29. model.label = 'hello';
  30. return validator.validate(model, { forbidUnknownValues: true }).then(errors => {
  31. console.log('errors', errors);
  32. });

验证分组

在定义DTO的时候,创建时不需要进行唯一标识的必填验证,但修改是需要判断的,其他的参数验证跟创建DTO一样。当然你也可以为不同场景创建多个DTO,但这样会增加维护成本,比如新增某些字段,你就得对去修改多个DTO代码文件。

那么有没有一种办法可以只维护一个DTO,然后根据不同场景决定使用哪些验证器。办法当然是有的。

  1. class UserDTO {
  2. @IsNotEmpty({groups:['put']})
  3. id: string;
  4. @IsNotEmpty({groups:['post','put']})
  5. username: string;
  6. @IsNotEmpty({groups:['post','put']})
  7. password:string;
  8. }
  9. const user = new UserDTO();
  10. user.username='123456';
  11. user.password='123456';
  12. //只对分组post进行验证,也就是不会使用id属性必填验证器
  13. return validator.validate(model, {groups:['post']}).then(errors => {
  14. console.log('errors', errors);
  15. });

验证器执行参数整理

  • groups 对验证器进行分组
  • forbidUnknownValues 翻译为禁止未知值,这个设置值比较令人迷惑,经过测试得出一个结论:执行验证时,如果forbidUnknownValues 设置为true并且目标对象找不到任何验证器,验证将无法通过,并提示:”unknownValue”:”an unknown value was passed to the validate function”
  • strictGroups 设置为true,将按照严格按照分组来使用验证器。
  • always 设置为true 验证器将不受分组的限制,始终会执行
  • 关于验证器执行参数还有其他设置,请自行探索,以上列举的是我目前常用的几个参数 ```typescript //============ forbidUnknownValues:true start ================= class UserDTO { username: string; password:string; }

const user = new UserDTO(); user.username=’123456’; user.password=’123456’; return validator.validate(user).then(errors => { console.log(‘errors’, errors); //“unknownValue”:”an unknown value was passed to the validate function” });

return validator.validate({name:”123”}).then(errors => { console.log(‘errors’, errors); //“unknownValue”:”an unknown value was passed to the validate function” });

class UserDTO { @IsNotEmpty({groups:[‘put’]}) id: string; @IsNotEmpty({groups:[‘post’,’put’]}) username: string; @IsNotEmpty({groups:[‘post’,’put’]}) password:string; } const user = new UserDTO(); user.username=’123456’; user.password=’123456’; //指定不存在的分组验证器,由于无任何验证器可以使用,所以也无法通过unknownValue验证器 return validator.validate(user, {groups:[‘delete’]}).then(errors => { console.log(‘errors’, errors); //“unknownValue”:”an unknown value was passed to the validate function” }); //============ forbidUnknownValues:true end =================

//================== strictGroups:true start ================== class UserDTO { @IsNotEmpty({groups:[‘put’]}) id: string; @IsNotEmpty() username: string; @IsNotEmpty() password:string; } const user = new UserDTO(); user.username=’123456’; user.password=’123456’; //打算只验证username和password return validator.validate(user).then(errors => { console.log(‘errors’, errors); //不符合预期:id验证器验证不通过 }); return validator.validate(user,{strictGroups:true}).then(errors => { console.log(‘errors’, errors); //符合预期:验证通过,只验证username和password }); //================== strictGroups:true end==================

//==================== always:true start ==================== class UserDTO { @IsNotEmpty({groups:[‘put’]}) id: string; @IsNotEmpty() username: string; @IsNotEmpty() password:string; } const user = new UserDTO(); user.id=’001’; //除了验证id以外,还要验证username和password return validator.validate(user,{groups:[‘put’]}).then(errors => { console.log(‘errors’, errors); //验证通过,不符合预期:除了验证id以外,还要验证username和password });

//除了验证id以外,还要验证username和password return validator.validate(user,{groups:[‘put’],always:true}).then(errors => { console.log(‘errors’, errors); //验证不通过,符合预期:验证id、username和password });

  1. 通过以上的几个例子,我们执行参数将会使用以下设置
  2. ```typescript
  3. //验证对象至少拥有一个验证器、按照严格的分组模式使用验证器(即分组传空,只执行没有分组的),如果使用分组验证器,希望同时验证没有分组的验证器
  4. return validator.validate(user,{groups:['put'],always:true,strictGroups:true,forbidUnknownValues:true}).then(errors => {
  5. console.log('errors', errors);
  6. });