【NestJS应用从0到1】7. 扩展验证装饰器支持敏感词过滤

213 阅读2分钟

在公共文章或者用户内容等需求,内容审核是一个必须的工作。我们可以在后台提交时验证内容的合法性,合法允许继续提交,不合法可以抛出异常返回给前端。

如果每个需要验证的地方在服务层去调用内容检查的方法,其实就脱离了单一职责的准则,所以我们完全可以在Controller层就进行判断。使用class-validator自定义验证器,扩展一个装饰器进行验证,这样我们在DTO中给属性添加装饰器即可,这样验证成功的内容才会继续前往service层进行后续操作。

在这之前

你需要知道

敏感词过滤最简单的实现方法就是,遍历敏感词使用Array.some 或者 includes语法。 我的项目使用sensitive-word-tool;一个基于 DFA 算法实现,支持过滤掉干扰词的基础上处理敏感词,非常轻巧完备的 JavaScript 敏感词处理库🚀🚀🚀

Code it

其实使用起来极其简单,使用class-validatorregisterDecorator扩展一个装饰器,然后在Dto中需要验证的属性添加上装饰器即可

定义装饰器

需要注意的是,下面我特意将 SensitiveWordTool 的实例化使用单例模式包装,防止每次进行验证时都需要进行实例化。当然这样会带来实例化持续存在而内存占用的副作用。按需使用。

import {
  registerDecorator,
  ValidationOptions,
  ValidatorConstraint,
  ValidatorConstraintInterface,
} from 'class-validator';
import SensitiveWordTool from 'sensitive-word-tool';

// 单例模式 - SensitiveWordTool
// 防止每次验证都需要重新实例化
class SingleSensitiveWordValidator {
  private static instance: SensitiveWordTool;
  // 私有构造函数,防止外部实例化
  private constructor() {}
  public static getInstance(option: any): SensitiveWordTool {
    if (!SingleSensitiveWordValidator.instance) {
      SingleSensitiveWordValidator.instance = new SensitiveWordTool(option);
    }
    return SingleSensitiveWordValidator.instance;
  }
}

@ValidatorConstraint({ async: false })
export class IsSensitiveConstraint implements ValidatorConstraintInterface {
  validate(text: string) {
    const sensitiveWordTool = SingleSensitiveWordValidator.getInstance({
      useDefaultWords: true, // 初始化时使用默认敏感词
    });
    return !sensitiveWordTool.verify(text); // 如果包含敏感词,则验证失败
  }
  defaultMessage() {
    return '输入内容包含敏感词汇,请修改后再提交。';
  }
}

export function SensitiveValid(validationOptions?: ValidationOptions) {
  return function (object: object, propertyName: string) {
    registerDecorator({
      name: 'SensitiveValid',
      target: object.constructor,
      propertyName: propertyName,
      options: validationOptions,
      validator: IsSensitiveConstraint,
    });
  };
}

使用

使用就更简单了,以用户昵称的Dto做示意

import { IsString, Length } from 'class-validator';
import { SensitiveValid } from '@/decorators/sensitiveValid.decorator';

export class UserModifyNickName {
  @IsString()
  @Length(2, 6, { message: '昵称请控制在2-8个字' })
  @SensitiveValid({ message: '昵称不能包含敏感词' })
  nickName: string;
}

完结,撒花❀❀❀❀❀❀