阿里出品的 antd 和 ElementUI 組件庫中表單校驗默認使用的 async-validator,它在 gitbub 上也獲得了 3.8k 的 star,可見這個庫十分強大,奈何只有英文文檔看的蛋疼,因此花點時間翻譯一下以便日后查看和為新手同事提供文檔,原文都以折疊的方式保留着,看不懂我的描述可以展開看看原文。
結合 github 上的例子能方便理解
(大部分原因是我英文水平不夠,但是明明是中國人寫的為啥不順手寫個中文的 readme 呢,雖然就算翻譯成了中文也還是晦澀難懂。。。)
翻譯時間: 2019/5/31
正文開始。
async-validator
一個用於表單異步校驗的庫,參考了 https://github.com/freeformsystems/async-validate
Validate form asynchronous. A variation of https://github.com/freeformsystems/async-validate
API
下述內容來自於 async-validate. 的早期版本
The following is modified from earlier version of [async-validate](https://github.com/freeformsystems/async-validate).
Usage 使用方法
基本的使用方法:定義一個 descriptor,將它傳入 schema,得到一個 validator。將需要校驗的對象和回調傳入 validator.validate 方法中。
注:descriptor 是對校驗規則的描述,validator 是根據校驗規則得到的校驗器
Basic usage involves defining a descriptor, assigning it to a schema and passing the object to be validated and a callback function to the `validate` method of the schema:
var schema = require('async-validator');
var descriptor = {
name: {
type: "string",
required: true,
validator: (rule, value) => value === 'muji',
},
};
var validator = new schema(descriptor);
validator.validate({name: "muji"}, (errors, fields) => {
if(errors) {
// validation failed, errors is an array of all errors
// fields is an object keyed by field name with an array of
// errors per field
// 校驗未通過的情況,errors 是所有錯誤的數組
// fields 是一個 object,以字段作為 key 值,該字段對應的錯誤數組作為 value
// (其實 fields 就是把 errors 按照原對象的 key 值分組)
return handleErrors(errors, fields);
}
// validation passed
// 這里說明校驗已通過
});
// PROMISE USAGE
// Promise 式用法
validator.validate({
name: "muji",
asyncValidator: (rule, value) => axios.post('/nameValidator', { name: value }),
}, (errors, fields) => {
if(errors) {
// validation failed, errors is an array of all errors
// fields is an object keyed by field name with an array of
// errors per field
// 校驗未通過的情況,errors 和 fields 同上
return handleErrors(errors, fields);
}
// validation passed
// 校驗通過
})
.then(() => {
// validation passed
// 校驗通過
})
.catch(({ errors, fields }) => {
return handleErrors(errors, fields);
})
Validate 方法參數
function(source, [options], callback): Promise
source
: 需要校驗的對象(必填).options
: 校驗選項(可選).callback
: 校驗完成時的回調(必填).
* `source`: The object to validate (required). * `options`: An object describing processing options for the validation (optional). * `callback`: A callback function to invoke when validation completes (required).
方法返回一個 Promise 對象:
then()
,說明校驗通過catch({ errors, fields })
,校驗未通過,errors, fields 含義見前面示例
The method will return a Promise object like: * `then()`,validation passed * `catch({ errors, fields })`,validation failed, errors is an array of all errors, fields is an object keyed by field name with an array of
Options 選項
-
first
: Boolean, 遇見第一個未通過校驗的值時便調用callback
回調,不再繼續校驗剩余規則。
適用情況:校驗涉及到多個異步調用,比如數據庫查詢,而你只需要獲取首個校驗錯誤時 -
firstFields
: Boolean|String[], 對於指定字段,遇見第一條未通過的校驗規則時便調用callback
回調,而不再校驗該字段的其他規則 ,傳入true
代表所有字段。
* `first`: Boolean, Invoke `callback` when the first validation rule generates an error, no more validation rules are processed. If your validation involves multiple asynchronous calls (for example, database queries) and you only need the first error use this option.
firstFields
: Boolean|String[], Invokecallback
when the first validation rule of the specified field generates an error,
no more validation rules of the same field are processed.true
means all fields.
Rules
Rules 也可以是用於校驗的函數
Rules may be functions that perform validation.
function(rule, value, callback, source, options)
rule
: 當前校驗字段在 descriptor 中所對應的校驗規則,其中的 field 屬性是當前正在校驗字段的名稱value
: 當前校驗字段的值callback
: 在校驗完成時的回調,傳入Error
[或者是一個數組] 代表校驗失敗,如果校驗是同步的話,直接返回false
或Error
或Error
數組也可以(注:異步校驗通過時直接不帶參數調用callback()
,代表沒有錯誤)source
: 傳入validate
方法的 object,也就是需要校驗的對象options
: 傳入的額外選項options.messages
: 對象包含的校驗錯誤提示信息,會被合並到默認的提示信息中
* `rule`: The validation rule in the source descriptor that corresponds to the field name being validated. It is always assigned a `field` property with the name of the field being validated. * `value`: The value of the source object property being validated. * `callback`: A callback function to invoke once validation is complete. It expects to be passed an array of `Error` instances to indicate validation failure. If the check is synchronous, you can directly return a ` false ` or ` Error ` or ` Error Array `. * `source`: The source object that was passed to the `validate` method. * `options`: Additional options. * `options.messages`: The object containing validation error messages, will be deep merged with defaultMessages.
傳入 validate
或 asyncValidate
的 options 被帶到了校驗函數中,以便你可以在校驗函數中拿到數據(比如 model 引用)。然而,option中部分屬性名是被保留的,你如果使用了的話會被覆蓋掉,其中包括 messages
, exception
和 error
。
The options passed to `validate` or `asyncValidate` are passed on to the validation functions so that you may reference transient data (such as model references) in validation functions. However, some option names are reserved; if you use these properties of the options object they are overwritten. The reserved properties are `messages`, `exception` and `error`.
var schema = require('async-validator');
var descriptor = {
name(rule, value, callback, source, options) {
var errors = [];
if(!/^[a-z0-9]+$/.test(value)) {
errors.push(
new Error(
util.format("%s must be lowercase alphanumeric characters",
rule.field)));
}
return errors;
}
}
var validator = new schema(descriptor);
validator.validate({name: "Firstname"}, (errors, fields) => {
if(errors) {
return handleErrors(errors, fields);
}
// validation passed
});
在需要對一個字段設置多條校驗規則時,可以把規則設為一個數組,比如
It is often useful to test against multiple validation rules for a single field, to do so make the rule an array of objects, for example:
var descriptor = {
email: [
{type: "string", required: true, pattern: schema.pattern.email},
{validator(rule, value, callback, source, options) {
var errors = [];
// test if email address already exists in a database
// and add a validation error to the errors array if it does
return errors;
}}
]
}
Type 內置類型
下列是 type
可用的值:
string
: 必須是string
.This is the default type.
number
: 必須是number
.boolean
: 必須是boolean
.method
: 必須是function
.regexp
: 必須是正則或者是在調用new RegExp
時不報錯的字符串.integer
: 整數.float
: 浮點數.array
: 必須是數組,通過Array.isArray
判斷.object
: 是對象且不為數組.enum
: 值必須出現在enmu
枚舉值中.date
: 合法的日期,使用Date
判斷url
: url.hex
: 16進制.email
: 郵箱地址.
Indicates the `type` of validator to use. Recognised type values are:
string
: Must be of typestring
.This is the default type.
number
: Must be of typenumber
.boolean
: Must be of typeboolean
.method
: Must be of typefunction
.regexp
: Must be an instance ofRegExp
or a string that does not generate an exception when creating a newRegExp
.integer
: Must be of typenumber
and an integer.float
: Must be of typenumber
and a floating point number.array
: Must be an array as determined byArray.isArray
.object
: Must be of typeobject
and notArray.isArray
.enum
: Value must exist in theenum
.date
: Value must be valid as determined byDate
url
: Must be of typeurl
.hex
: Must be of typehex
.email
: Must be of typeemail
.
Required
required
屬性代表這個字段必須出現在對象中
The `required` rule property indicates that the field must exist on the source object being validated.
Pattern
pattern
屬性代表需要符合的正則
The `pattern` rule property indicates a regular expression that the value must match to pass validation.
Range
使用 min
和 max
屬性定義范圍,對於字符串和數組會與 value.length
比較,對於數字會直接與值比較
A range is defined using the `min` and `max` properties. For `string` and `array` types compariso is performed against the `length`, for `number` types the number must not be less than `min` nor greater than `max`.
Length
使用 len
屬性直接指定長度,會與字符串和數組的 value.length
比較相等,對於數字會直接與值比較是否相等
如果 len
與 min
和 max
同時使用, len
優先。
To validate an exact length of a field specify the `len` property. For `string` and `array` types comparison is performed on the `length` property, for the `number` type this property indicates an exact match for the `number`, ie, it may only be strictly equal to `len`.
If the len
property is combined with the min
and max
range properties, len
takes precedence.
Enumerable
可枚舉值
對於可以枚舉出所有情況的類型,可以使用枚舉校驗,如下:
To validate a value from a list of possible values use the `enum` type with a `enum` property listing the valid values for the field, for example:
var descriptor = {
role: {type: "enum", enum: ['admin', 'user', 'guest']}
}
Whitespace
把僅包含空格的字段視為錯誤是很典型的做法,為了額外測試字段是否只有空格,添加 whitespace
屬性並設為true。這個屬性要求字段必須為 string
類型。
如果你想要修正用戶的輸入而不是測試有無空格,查看 transform 中去除空格的例子。
It is typical to treat required fields that only contain whitespace as errors. To add an additional test for a string that consists solely of whitespace add a `whitespace` property to a rule with a value of `true`. The rule must be a `string` type.
You may wish to sanitize user input instead of testing for whitespace, see transform for an example that would allow you to strip whitespace.
Deep Rules 深層規則
如果需要校驗一個深層的對象,你需要使用 fields
屬性來設置嵌套的規則
If you need to validate deep object properties you may do so for validation rules that are of the `object` or `array` type by assigning nested rules to a `fields` property of the rule.
var 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}
}
var validator = new schema(descriptor);
validator.validate({ address: {} }, (errors, fields) => {
// errors for address.street, address.city, address.zip
});
需要注意的是,如果沒有在父規則上指定 required
屬性,在校驗對象中不存在這個屬性是完全合法的,嵌套的深層規則也不會運行。
Note that if you do not specify the `required` property on the parent rule it is perfectly valid for the field not to be declared on the source object and the deep validation rules will not be executed as there is nothing to validate against.
深層規則提供了直接一個定義嵌套規則的方式,讓你可以簡化傳遞給 schema.validate()
的 options
。
Deep rule validation creates a schema for the nested rules so you can also specify the `options` passed to the `schema.validate()` method.
var descriptor = {
address: {
type: "object", required: true, options: {single: true, first: 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}
}
var validator = new schema(descriptor);
validator.validate({ address: {} })
.catch(({ errors, fields }) => {
// now only errors for street and name
});
如果你像下面這樣寫,父規則也會被校驗
The parent rule is also validated so if you have a set of rules such as:
var 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"]}
會產生兩個錯誤,一個是數組長度不匹配,一個是缺少了索引為 2
的元素
And supply a source object of `{roles: ["admin", "user"]}` then two errors will be created. One for the array length mismatch and one for the missing required array entry at index 2.
defaultField 默認字段
defaultField
屬性可以在 array
和 object
類型中用於校驗所有的值,它可以是一個包含有校驗規則的對象或數組。 例子如下:
The `defaultField` property can be used with the `array` or `object` type for validating all values of the container. It may be an `object` or `array` containing validation rules. For example:
var descriptor = {
urls: {
type: "array", required: true,
defaultField: {type: "url"}
}
}
注意,defaultField
是 fields
的擴展,見 deep rules.
Note that `defaultField` is expanded to `fields`, see [deep rules](#deep-rules).
Transform 變換
有時候需要在校驗前修改值,強制修改為特定格式。 為此在校驗規則中添加了 transform
, 這個屬性會在校驗前執行,以適當的方式改變原始對象的值。(也就是返回值會作用在原始對象的值上)
Sometimes it is necessary to transform a value before validation, possibly to coerce the value or to sanitize it in some way. To do this add a `transform` function to the validation rule. The property is transformed prior to validation and re-assigned to the source object to mutate the value of the property in place.
var schema = require('async-validator');
var sanitize = require('validator').sanitize;
var descriptor = {
name: {
type: "string",
required: true, pattern: /^[a-z]+$/,
transform(value) {
return sanitize(value).trim();
}
}
}
var validator = new schema(descriptor);
var source = {name: " user "};
validator.validate(source)
.then(() => assert.equal(source.name, "user"));
如果沒有 transform
函數校驗會失敗因為前后空格導致正則與輸入不符,但在添加了 transform
函數后便可通過因為字段已經被清洗了(或者翻譯為使輸入值符合預期格式)
Without the `transform` function validation would fail due to the pattern not matching as the input contains leading and trailing whitespace, but by adding the transform function validation passes and the field value is sanitized at the same time.
Messages 提示信息
在某些需求下,你可能需要格式化支持或者想要不同校驗錯誤信息。
最簡單的方式就是直接為 message
屬性賦值:
Depending upon your application requirements, you may need i18n support or you may prefer different validation error messages. The easiest way to achieve this is to assign a `message` to a rule:
{name:{type: "string", required: true, message: "Name is required"}}
消息可以是任意類型的,比如 JSX
:
Message can be any type, such as jsx format.
{name:{type: "string", required: true, message: <b>Name is required</b>}}
也可以是函數,比如使用 vue-i18n 時:
Message can also be a function, e.g. if you use vue-i18n:
{name:{type: "string", required: true, message: () => this.$t( 'name is required' )}}
有時候你只是需要對相同的校驗規則定義不同語言的提示信息,在這種情況下為各種語言重復定義信息就顯得很多余。
你也可以采取這個方案:定義你自己的提示信息並賦值給 schema
:
Potentially you may require the same schema validation rules for different languages, in which case duplicating the schema rules for each language does not make sense.
In this scenario you could just provide your own messages for the language and assign it to the schema:
var schema = require('async-validator');
var cn = {
required: '%s 必填',
};
var descriptor = {name:{type: "string", required: true}};
var validator = new schema(descriptor);
// deep merge with defaultMessages
validator.messages(cn);
...
如果你要定義自己的校驗函數,最好將提示信息賦值給消息對象,並在校驗函數中通過 options.messages
訪問消息。(說實話我沒看懂是什么意思,應該是指不要把消息硬編碼寫在校驗函數里面而是通過option傳遞,以便修改)
If you are defining your own validation functions it is better practice to assign the message strings to a messages object and then access the messages via the `options.messages` property within the validation function.
asyncValidator 異步校驗函數
你可以對指定的字段自定義包含異步操作的校驗函數
You can customize the asynchronous validation function for the specified field:
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 校驗函數
你也可像下面這樣自定義校驗函數:
you can custom validate function for specified field:
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'),
];
}
},
};
FAQ
How to avoid warning 如何關閉警告
var Schema = require('async-validator');
Schema.warning = function(){};