为了账号安全,请及时绑定邮箱和手机立即绑定

【金秋打卡】第21天 使用装饰器完成用户输入验证

标签:
Node.js

课程名称web前端架构师
课程章节:第16周 第八章 编写作品相关代码以及学习装饰器的使用
主讲老师:张轩
课程内容:使用装饰器完成用户输入验证

装饰器的使用

TypeScript 中的装饰器使用 @expression 这种形式,expression 求值后为一个函数,它在运行时被调用,被装饰的声明信息会被做为参数传入。

Javascript规范里的装饰器目前处在 建议征集的第二阶段,https://github.com/tc39/proposal-decorators 也就意味着不能在原生代码中直接使用,浏览器暂不支持。

TypeScript 工具在编译阶段,把装饰器语法转换成浏览器可执行的代码。

// tsconfig
{
  "compilerOptions": {
     "experimentalDecorators": true
  }
}

装饰器的基本使用

这里我们主要使用的是方法装饰器, 其他的可以参考。https://es6.ruanyifeng.com/#docs/decorator

装饰器的本意是要“装饰”类的实例,但是这个时候实例还没生成,所以只能去装饰原型(这不同于类的装饰,那种情况时target参数指的是类本身);第二个参数是所要装饰的属性名,第三个参数是该属性的描述对象。

function fn1(prototype: any, propertyKey: string, descriptor: PropertyDescriptor) {
  console.log('fn', prototype, propertyKey, descriptor);
}
function fn2(prototype: any, propertyKey: string, descriptor: PropertyDescriptor) {
  console.log('fn2', prototype, propertyKey, descriptor);
}
class Car {
  constructor() {
    console.log('car');
  }
  @fn1
  @fn2
  run() {
    console.log('run');
  }
}
const car = new Car();
car.run();

运行后, 发现 fn2 装饰器先运行

可以通过修改 descriptor 的值来修改装饰的方法

function fn1(prototype: any, propertyKey: string, descriptor: PropertyDescriptor) {
  console.log('fn', prototype, propertyKey, descriptor);
  const originMethod = descriptor.value;
  descriptor.value = function() {
    console.log('fn start');
    originMethod.call(this);
    console.log('fn end');
  };
}
function fn2(prototype: any, propertyKey: string, descriptor: PropertyDescriptor) {
  console.log('fn2', prototype, propertyKey, descriptor);
  // 获取装饰方法的本身
  const originMethod = descriptor.value;
  descriptor.value = function() {
    console.log('fn2 start');
    originMethod.call(this);
    console.log('fn2 end');
  };
}
class Car {
  constructor() {
    console.log('car');
  }
  @fn1
  @fn2
  run() {
    console.log('run');
  }
}
const car = new Car();
car.run();

通过工厂函数可以给装饰器传递参数

function fn1(num: number) {
  return (prototype: any, propertyKey: string, descriptor: PropertyDescriptor) => {
    console.log('fn', prototype, propertyKey, descriptor);
    const originMethod = descriptor.value;
    descriptor.value = function() {
      originMethod.call(this);
    };
  };
}
function fn2() {
  return (prototype: any, propertyKey: string, descriptor: PropertyDescriptor) => {
    console.log('fn2', prototype, propertyKey, descriptor);
    const originMethod = descriptor.value;
    descriptor.value = function() {
      originMethod.call(this);
    };
  };
}
class Car {
  constructor() {
    console.log('car');
  }
  @fn1(123)
  @fn2(222)
  run() {
    console.log('run');
  }
}
const car = new Car();
car.run();

使用装饰器完成用户输入验证

首先将错误信息提取到一个目录下,新建一个 error 目录

// app/error/index.ts
import userErrMsg from './user';
import workErrMsg from './work';

const errMsg = {
  ...userErrMsg,
  ...workErrMsg,
};
export default errMsg;

// app/error/user.ts
export default {
  userValidateFail: {
    errno: 101001,
    msg: '输入信息验证失败',
  },
  createUserExists: {
    errno: 101002,
    msg: '该邮箱已经注册过,请直接登录',
  },
  signinValidateFail: {
    errno: 101003,
    msg: '用户名或密码错误',
  },
  signValidateFail: {
    errno: 101004,
    msg: '用户验证失败',
  },
  phoneValidateFail: {
    errno: 101005,
    msg: '手机号格式错误',
  },
  sendCodeFrequentlyFail: {
    errno: 101006,
    msg: '请勿频繁获取短信验证码',
  },
  signinCorrectCodeFail: {
    errno: 101007,
    msg: '验证码输入错误',
  },
};
// app/error/work.ts
export default {
  workValidateFail: {
    errno: 102001,
    msg: '输入信息验证失败',
  },
};

用户输入验证装饰器

首先我们期望我们使用时下面这种方式使用的,只需要传入 rules 和 errMsg ,就可以通过装饰器进行校验

import { Controller } from 'egg';

const rules = {
  title: {
    requireds: true,
    type: 'string',
  },
};

export default class Work extends Controller {
  @inputValidate(rules, 'workValidateFail')
  async createWork() {
    const { ctx, service } = this;
    const res = await service.work.createWork();
    ctx.helper.success({ ctx, res });
  }
}

编写装饰器代码

// app/decorator/inputValidate.ts
import { Controller } from 'egg';
import { errMsgType } from '../error';

const inputValidate = (rules: any, errType: errMsgType) => {
  return (_target, _propertyKey, descriptor: PropertyDescriptor) => {
    const originMethod = descriptor.value;
    descriptor.value = function(...args:any[]) {
      const that = this as Controller;
      // 这里 ts 会报错,但是我们知道我们是在 Controller 类或子类的方法中运行的,所以把它给忽略掉
      // eslint-disable-next-line @typescript-eslint/ban-ts-comment
      // @ts-ignore
      const { ctx, app } = that;
      const errors = app.validator.validate(rules, ctx.request.body);
      if (errors) {
      // 出现错误,直接返回错误信息
        return ctx.helper.error({ ctx, errType, err: errors });
      }
      // 调用属性本身的方法
      return originMethod.apply(this, args);
    };
  };
};

export default inputValidate;

然后运行代码,就可以通过装饰器的形式 @inputValidate(rules, 'workValidateFail') 来进行参数校验,所有的参数校验也都可以这样使用,大大提高了代码的复用。

图片描述

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消