在公共文章或者用户内容等需求,内容审核是一个必须的工作。我们可以在后台提交时验证内容的合法性,合法允许继续提交,不合法可以抛出异常返回给前端。
如果每个需要验证的地方在服务层去调用内容检查的方法,其实就脱离了单一职责的准则,所以我们完全可以在Controller层就进行判断。使用class-validator自定义验证器,扩展一个装饰器进行验证,这样我们在DTO中给属性添加装饰器即可,这样验证成功的内容才会继续前往service层进行后续操作。
在这之前
你需要知道
敏感词过滤最简单的实现方法就是,遍历敏感词使用Array.some 或者 includes语法。 我的项目使用
sensitive-word-tool;一个基于 DFA 算法实现,支持过滤掉干扰词的基础上处理敏感词,非常轻巧完备的 JavaScript 敏感词处理库🚀🚀🚀
Code it
其实使用起来极其简单,使用class-validator的registerDecorator扩展一个装饰器,然后在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;
}