Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

MageeLin/async-validator-source-code-analysis

Open more actions menu
 
 

Repository files navigation

async-validator

NPM version build status Test coverage node version npm download npm bundle size (minified + gzip)

内容介绍

本仓库 fork 自async-validatormaster分支,commitID8e17b30。翻译了 README 文档,并解析了源代码。

代码结构

代码结构如下所示:

代码结构图

异步表单验证。参考自 https://github.com/freeformsystems/async-validate

安装

npm i async-validator

使用方法

基本的使用方法:定义一个 descriptor,将其分配给一个 schema,并将要验证的 object 以及一个回调函数传递给该 schemavalidate方法:

import Schema from 'async-validator';
const descriptor = {
  name: {
    type: 'string',
    required: true,
    validator: (rule, value) => value === 'muji',
  },
  age: {
    type: 'number',
    asyncValidator: (rule, value) => {
      return new Promise((resolve, reject) => {
        if (value < 18) {
          reject('too young'); // reject 这个 error message
        } else {
          resolve();
        }
      });
    },
  },
};
const validator = new Schema(descriptor);
validator.validate({ name: 'muji' }, (errors, fields) => {
  if (errors) {
    // 校验失败,errors是一个包含所有error的数组。
    // fields是一个对象,对象中field(字段)是key,每个field对应的所有error组成的数组是value。
    return handleErrors(errors, fields);
  }
  // 校验通过
});

// PROMISE使用方法
validator
  .validate({ name: 'muji', age: 16 })
  .then(() => {
    // 校验通过或者没有error message
  })
  .catch(({ errors, fields }) => {
    return handleErrors(errors, fields);
  });

API

Validate

function(source, [options], callback): Promise
  • source: 需要校验的对象(必选)。
  • options: 描述校验的处理选项的对象(可选)。
  • callback: 当校验完成时调用的回调函数(必选)。

该方法将返回一个 Promise 对象,如下:

  • then(),校验通过
  • catch({ errors, fields }),校验失败,errors 是一个所有 error 组成的数组;field 是一个对象,键是 field,值是对应的 errors 数组。

Options

  • suppressWarning: Boolean,指示是否取消关于无效值的内部警告。

  • first: Boolean,当第一个校验规则产生error时调用callback,不再继续处理校验规则。如果校验涉及多个异步调用(例如数据库查询) ,且只需要第一个error出现时就停止,则使用此选项。

  • firstFields: Boolean|String[], 当指定字段的第一个校验规则产生error时调用callback,不再继续处理相同字段的校验规则。true意味着所有字段生效。

Rules

Rules 是执行校验的函数。

function(rule, value, callback, source, options)
  • rule: 在源descriptor中,与要校验的字段名称相对应的校验规则。始终为它分配一个field属性,其中包含要验证的字段的名称。
  • value: 源对象属性中要校验的值。
  • callback: 校验完成后要调用的callback。它期望传递一个Error实例数组以指示校验失败。如果校验是同步的,则可以直接返回falseErrorError Array
  • source: 传给validate 方法的源对象。
  • options: 额外选项。
  • options.messages: 包含校验 error message 的对象,将与 defaultMessages 进行深度合并。

传给validateasyncValidate的选项被传递给校验函数,以便于可以在校验函数中引用临时数据(例如 model 引用)。但是有些选项名是保留的; 如果使用options对象的这些属性将会导致覆盖。保留的属性是messages, exceptionerror.

import Schema from 'async-validator';
const descriptor = {
  name(rule, value, callback, source, options) {
    const errors = [];
    if (!/^[a-z0-9]+$/.test(value)) {
      errors.push(
        new Error(util.format('%s 必须为小写字母的数字字符', rule.field)),
      );
    }
    return errors;
  },
};
const validator = new Schema(descriptor);
validator.validate({ name: 'Firstname' }, (errors, fields) => {
  if (errors) {
    return handleErrors(errors, fields);
  }
  // 校验通过
});

对单个字段的多个校验规则进行测试的情况很常见,此时可以使规则成为一个对象数组,例如:

const descriptor = {
  email: [
    { type: 'string', required: true, pattern: Schema.pattern.email },
    {
      validator(rule, value, callback, source, options) {
        const errors = [];
        // 测试email地址是否已经在数组库中存在
        // 并当已存在时在errors数组中添加一个error
        return errors;
      },
    },
  ],
};

Type

标志要使用的validatortype,可识别的type值为:

  • string: 必须为类型 string, 这是默认的 type。
  • number: 必须为类型 number
  • boolean: 必须为类型 boolean
  • method: 必须为类型 function
  • regexp: 必须为 RegExp的实例 或者 一个string,使用它 new RegExp时不能报错。
  • integer: 必须为类型 number 并且是一个整数。
  • float: 必须为类型 number 并且是一个浮点数。
  • array: 必须是一个 array ,由 Array.isArray确定。
  • object: 必须为类型 object 并且 Array.isArray返回false
  • enum: 值必须在 enum中存在。
  • date: 值必须是有效的,由Date确定。
  • url: 必须为类型 url
  • hex: 必须为类型 hex
  • email: 必须为类型 email
  • any: 可以是任意一种类型。

Required

rule属性required指示在校验时该field必须在source对象上存在

Pattern

rule属性pattern指示在校验时该值必须能通过正则表达式的校验。

Range

使用 minmax 属性定义范围。对于stringarray类型,根据length属性进行比较,对于number类型,数字不能小于 min,也不能大于 max

Length

若要校验field的精确长度,请指定 len 属性。对于stringarray类型的比较是在length属性上执行的,对于number类型,这个属性表示数字的精确匹配,也就是说,它只能严格等于 len

如果 len 属性与 minmax 两个 range 属性组合使用,len 优先。

Enumerable

从 3.0.0 版本开始,如果想校验enum类型中的值0false,就必须显式地包含它们。

要校验 所有可能值组成的列表 中的值,使用enum类型和enum属性列出该字段的有效值,例如:

const descriptor = {
  role: { type: 'enum', enum: ['admin', 'user', 'guest'] },
};

Whitespace

通常将只包含空白的必填字段视为错误。若要为仅由空格组成的字符串添加额外的校验,请向值为 true 的规则添加whitespace属性。规则必须是string类型。

您可能希望对用户输入进行净化,而不是校验whitespace,请参阅transform,以了解删除空白的示例。

Deep Rules

如果需要校验深层次对象的属性,可以通过将嵌套规则分配给rulesfields属性来校验属于 objectarray 类型的校验规则。

const descriptor = {
  address: {
    type: 'object',
    required: true,
    fields: {
      street: { type: 'string', required: true },
      city: { type: 'string', required: true },
      zip: { type: 'string', required: true, len: 8, message: 'invalid zip' },
    },
  },
  name: { type: 'string', required: true },
};
const validator = new Schema(descriptor);
validator.validate({ address: {} }, (errors, fields) => {
  // address.street, address.city, address.zip产生了errors
});

请注意,如果没有在父规则上指定required属性,那么不在source对象上声明该字段是完全有效的,并且深度校验规则也不会执行,因为没有什么需要校验的东西。

深度规则校验为嵌套rules创建schema,因此还可以指定传递给 schema.validate()方法的options

const descriptor = {
  address: {
    type: 'object',
    required: true,
    options: { first: true },
    fields: {
      street: { type: 'string', required: true },
      city: { type: 'string', required: true },
      zip: { type: 'string', required: true, len: 8, message: '无效zip' },
    },
  },
  name: { type: 'string', required: true },
};
const validator = new Schema(descriptor);

validator.validate({ address: {} }).catch(({ errors, fields }) => {
  // 只有street 和 name 产生了 errors
});

父规则也会被校验,所以如果你有一组rules,比如:

const descriptor = {
  roles: {
    type: 'array',
    required: true,
    len: 3,
    fields: {
      0: { type: 'string', required: true },
      1: { type: 'string', required: true },
      2: { type: 'string', required: true },
    },
  },
};

提供{ roles: ['admin', 'user'] }这样的source对象,将创建两个error。一个用于数组长度不匹配,另一个用于缺少索引2处所需的数组。

defaultField

defaultField 属性可以与arrayobject类型一起使用,以校验内部的所有值。它可能是包含校验规则的 objectarray。例如:

const descriptor = {
  urls: {
    type: 'array',
    required: true,
    defaultField: { type: 'url' },
  },
};

注意,若将defaultField 扩展为fields,请参见deep rules

Transform

有时需要校验之前转换值,可能是为了强制转换类型或以某种方式对其进行净化。为此,在校验规则中添加一个transform函数。在校验之前对属性进行转换,并重新分配给source对象以更改属性的值。

import Schema from 'async-validator';
const descriptor = {
  name: {
    type: 'string',
    required: true,
    pattern: /^[a-z]+$/,
    transform(value) {
      return value.trim();
    },
  },
};
const validator = new Schema(descriptor);
const source = { name: ' user  ' };
validator.validate(source).then(() => assert.equal(source.name, 'user'));

如果没有transform函数,校验将会失败,原因是pattern不匹配,因为输入包含前置和后置空白。但是通过添加transform函数,校验成功并且字段值同时被净化。

Messages

根据应用的需求,可能需要 i18n 支持,或者可能更喜欢个性化的校验error message

最简单的方法是给一个rule分配一条message:

{ name: { type: 'string', required: true, message: '请填写名称' } }

message可以是任意类型,比如jsx格式:

{ name: { type: 'string', required: true, message: '<b>请填写名称</b>' } }

message也可以是一个函数,比如vue-i18n

{ name: { type: 'string', required: true, message: () => this.$t( '请填写名称' ) } }

对于不同的语言,可能需要相同的schema校验规则,在这种情况下,为每种语言复制schema规则是无效的。

但是,可以提供你自己的语言版本的message,然后把它分配给schema:

import Schema from 'async-validator';
const cn = {
  required: '请填写 %s',
};
const descriptor = { name: { type: 'string', required: true } };
const validator = new Schema(descriptor);
// 与defaultMessages深度合并
validator.messages(cn);
...

如果要自定义校验函数,最好将message字符串分配给message对象,然后通过校验函数中的 options.messages 属性访问message

asyncValidator

可以为指定字段自定义异步校验函数:

const fields = {
  asyncField: {
    asyncValidator(rule, value, callback) {
      ajax({
        url: 'xx',
        value: value,
      }).then(
        function (data) {
          callback();
        },
        function (error) {
          callback(new Error(error));
        },
      );
    },
  },

  promiseField: {
    asyncValidator(rule, value) {
      return ajax({
        url: 'xx',
        value: value,
      });
    },
  },
};

validator

可以为指定的字段自定义同步校验函数:

const fields = {
  field: {
    validator(rule, value, callback) {
      return value === 'test';
    },
    message: 'Value is not equal to "test".',
  },

  field2: {
    validator(rule, value, callback) {
      return new Error(`${value} is not equal to 'test'.`);
    },
  },

  arrField: {
    validator(rule, value) {
      return [new Error('Message 1'), new Error('Message 2')];
    },
  },
};

常见问题

如何取消 warning

import Schema from 'async-validator';
Schema.warning = function () {};

如果检查布尔值 true

使用 enum 类型 并传布尔值 true 参数作为选项。

{
  type: 'enum',
  enum: [true],
  message: '',
}

测试用例

npm test

测试覆盖率

npm run coverage

打开 coverage/ dir

许可证

一切内容皆适用 MIT 许可证。

About

async-validator 异步校验库源码分析

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages

Morty Proxy This is a proxified and sanitized view of the page, visit original site.