validate.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. var pattern = {
  2. email: /^\S+?@\S+?\.\S+?$/,
  3. url: new RegExp("^(?!mailto:)(?:(?:http|https|ftp)://|//)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-*)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-*)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?$", 'i')
  4. };
  5. const FORMAT_MAPPING = {
  6. "int": 'number',
  7. "bool": 'boolean',
  8. "double": 'number',
  9. "long": 'number'
  10. }
  11. function formatMessage(args, resources) {
  12. var defaultMessage = ['label']
  13. defaultMessage.forEach((item) => {
  14. if (args[item] === undefined) {
  15. args[item] = ''
  16. }
  17. })
  18. let str = resources
  19. for (let key in args) {
  20. let reg = new RegExp('{' + key + '}')
  21. str = str.replace(reg, args[key])
  22. }
  23. return str
  24. }
  25. function isEmptyValue(value, type) {
  26. if (value === undefined || value === null) {
  27. return true;
  28. }
  29. if (typeof value === 'string' && !value) {
  30. return true;
  31. }
  32. if (Array.isArray(value) && !value.length) {
  33. return true;
  34. }
  35. if (type === 'object' && !Object.keys(value).length) {
  36. return true;
  37. }
  38. return false;
  39. }
  40. class RuleValidator {
  41. constructor(message) {
  42. this._message = message
  43. }
  44. async validateRule(key, value, data) {
  45. return new Promise(async (resolve, reject) => {
  46. let result = await this._invokeValidate(key, value, data)
  47. if (result == null) {
  48. resolve(1)
  49. } else {
  50. reject(new Error(result))
  51. }
  52. })
  53. }
  54. async _invokeValidate(key, value, data) {
  55. var result = null
  56. let rules = key.rules
  57. let hasRequired = rules.findIndex((item) => { return item.required })
  58. if (hasRequired < 0) {
  59. if (value === null || value === undefined) {
  60. return result
  61. }
  62. if (typeof value === 'string' && !value.length) {
  63. return result
  64. }
  65. }
  66. var message = this._message
  67. if (rules === undefined) {
  68. return message['default']
  69. }
  70. for (var i = 0; i < rules.length; i++) {
  71. let rule = rules[i]
  72. let vt = this._getValidateType(rule)
  73. if (key.label !== undefined) {
  74. Object.assign(rule, { label: key.label })
  75. }
  76. if (RuleValidatorHelper[vt]) {
  77. result = RuleValidatorHelper[vt](rule, value, message)
  78. if (result != null) {
  79. break
  80. }
  81. }
  82. if (rule.validateFunction) {
  83. result = await this._validateFunction(rule, value, data, vt)
  84. if (result != null) {
  85. break
  86. }
  87. }
  88. }
  89. return result
  90. }
  91. async _validateFunction(rule, value, data, vt) {
  92. let result = null
  93. let isAsync = Object.prototype.toString.call(rule.validateFunction).includes('AsyncFunction')
  94. if (isAsync) {
  95. try {
  96. const p = await rule.validateFunction(rule, value, data)
  97. if (p) {
  98. result = this._getMessage(rule, p, vt)
  99. }
  100. } catch (e) {
  101. result = this._getMessage(rule, e.message, vt)
  102. }
  103. } else {
  104. let callback = null
  105. let callbackMessage = null
  106. let res = rule.validateFunction(rule, value, data, (message) => {
  107. callback = message
  108. })
  109. if (callback) {
  110. res = !(callback instanceof Error)
  111. callbackMessage = res ? callback : callback.message
  112. }
  113. if (!res) {
  114. result = this._getMessage(rule, callbackMessage, vt)
  115. }
  116. }
  117. return result
  118. }
  119. _getMessage(rule, message, vt) {
  120. return formatMessage(rule, message || rule.errorMessage || this._message[vt] || message['default'])
  121. }
  122. _getValidateType(rule) {
  123. // TODO
  124. var result = ''
  125. if (rule.required) {
  126. result = 'required'
  127. } else if (rule.enum) {
  128. result = 'range'
  129. } else if (rule.maximum || rule.minimum) {
  130. result = 'rangeNumber'
  131. } else if (rule.maxLength || rule.minLength) {
  132. result = 'rangeString'
  133. } else if (rule.format) {
  134. result = 'format'
  135. } else if (rule.pattern) {
  136. result = 'pattern'
  137. } else if (rule.validate) {
  138. result = 'validate'
  139. }
  140. return result
  141. }
  142. }
  143. const RuleValidatorHelper = {
  144. required(rule, value, message) {
  145. if (rule.required && isEmptyValue(value, rule.format || typeof value)) {
  146. return formatMessage(rule, rule.errorMessage || message.required);
  147. }
  148. return null
  149. },
  150. range(rule, value, message) {
  151. if (Array.isArray(value)) {
  152. return formatMessage(rule, rule.errorMessage || message['pattern'].mismatch);
  153. }
  154. if (rule.enum.indexOf(value) < 0) {
  155. return formatMessage(rule, message['enum']);
  156. }
  157. return null
  158. },
  159. rangeNumber(rule, value, message) {
  160. // let { minimum, maximum, exclusiveMinimum, exclusiveMaximum } = rule;
  161. let min = rule.minimum;
  162. let max = rule.maximum;
  163. let exclusiveMin = rule.exclusiveMinimum;
  164. let exclusiveMax = rule.exclusiveMaximum;
  165. let val = value;
  166. if (!types.number(val)) {
  167. return formatMessage(rule, rule.errorMessage || message['pattern'].mismatch);
  168. }
  169. let _min = exclusiveMin ? val <= min : val < min;
  170. let _max = exclusiveMax ? val >= max : val > max;
  171. if (min !== undefined && _min) {
  172. return formatMessage(rule, rule.errorMessage || message['number'].min)
  173. } else if (max !== undefined && _max) {
  174. return formatMessage(rule, rule.errorMessage || message['number'].max)
  175. } else if (min !== undefined && max !== undefined && (_min || _max)) {
  176. return formatMessage(rule, rule.errorMessage || message['number'].range)
  177. }
  178. return null
  179. },
  180. rangeString(rule, value, message) {
  181. let min = rule.minLength;
  182. let max = rule.maxLength;
  183. let val = value.length;
  184. if (typeof value !== 'string') {
  185. return formatMessage(rule, rule.errorMessage || message['pattern'].mismatch);
  186. }
  187. if (min !== undefined && val < min) {
  188. return formatMessage(rule, rule.errorMessage || message['string'].min)
  189. } else if (max !== undefined && val > max) {
  190. return formatMessage(rule, rule.errorMessage || message['string'].max)
  191. } else if (min !== undefined && max !== undefined && (val < min || val > max)) {
  192. return formatMessage(rule, rule.errorMessage || message['string'].range)
  193. }
  194. return null
  195. },
  196. pattern(rule, value, message) {
  197. if (!types['pattern'](rule.pattern, value)) {
  198. return formatMessage(rule, rule.errorMessage || message.pattern.mismatch);
  199. }
  200. return null
  201. },
  202. format(rule, value, message) {
  203. var customTypes = Object.keys(types);
  204. var format = FORMAT_MAPPING[rule.format] ? FORMAT_MAPPING[rule.format] : rule.format;
  205. if (customTypes.indexOf(format) > -1) {
  206. if (!types[format](value)) {
  207. return formatMessage(rule, rule.errorMessage || message.types[format]);
  208. }
  209. }
  210. return null
  211. }
  212. }
  213. const types = {
  214. integer(value) {
  215. return types.number(value) && parseInt(value, 10) === value;
  216. },
  217. string(value) {
  218. return typeof value === 'string';
  219. },
  220. number(value) {
  221. if (isNaN(value)) {
  222. return false;
  223. }
  224. return typeof value === 'number';
  225. },
  226. "boolean": function (value) {
  227. return typeof value === 'boolean';
  228. },
  229. "float": function (value) {
  230. return types.number(value) && !types.integer(value);
  231. },
  232. array(value) {
  233. return Array.isArray(value);
  234. },
  235. object(value) {
  236. return typeof value === 'object' && !types.array(value);
  237. },
  238. date(value) {
  239. var v
  240. if (value instanceof Date) {
  241. v = value;
  242. } else {
  243. v = new Date(value);
  244. }
  245. return typeof v.getTime === 'function' && typeof v.getMonth === 'function' && typeof v.getYear === 'function' && !isNaN(v.getTime());
  246. },
  247. timestamp(value) {
  248. if (!this.integer(value) || Math.abs(value).toString().length > 16) {
  249. return false
  250. }
  251. return this.date(value);
  252. },
  253. email(value) {
  254. return typeof value === 'string' && !!value.match(pattern.email) && value.length < 255;
  255. },
  256. url(value) {
  257. return typeof value === 'string' && !!value.match(pattern.url);
  258. },
  259. pattern(reg, value) {
  260. try {
  261. return new RegExp(reg).test(value);
  262. } catch (e) {
  263. return false;
  264. }
  265. },
  266. method(value) {
  267. return typeof value === 'function';
  268. }
  269. }
  270. class SchemaValidator extends RuleValidator {
  271. constructor(schema, options) {
  272. super(SchemaValidator.message);
  273. this._schema = schema
  274. this._options = options || null
  275. }
  276. updateSchema(schema) {
  277. this._schema = schema
  278. }
  279. async validate(data) {
  280. var checkResult = this._checkField(data)
  281. if (checkResult) {
  282. return checkResult
  283. }
  284. var result = await this.invokeValidate(data, false)
  285. return result.length ? result[0] : null
  286. }
  287. async validateAll(data) {
  288. var checkResult = this._checkField(data)
  289. if (checkResult) {
  290. return checkResult
  291. }
  292. return await this.invokeValidate(data, true)
  293. }
  294. async validateUpdate(data) {
  295. var checkResult = this._checkField(data)
  296. if (checkResult) {
  297. return checkResult
  298. }
  299. var result = await this.invokeValidateUpdate(data, false)
  300. return result.length ? result[0] : null
  301. }
  302. async invokeValidate(data, all) {
  303. let result = []
  304. let schema = this._schema
  305. for (let key in schema) {
  306. let value = schema[key]
  307. try {
  308. await this.validateRule(value, data[key], data)
  309. } catch (error) {
  310. result.push({
  311. key: key,
  312. errorMessage: error.message
  313. })
  314. if (!all) break
  315. }
  316. }
  317. return result
  318. }
  319. async invokeValidateUpdate(data, all) {
  320. let result = []
  321. for (let key in data) {
  322. try {
  323. await this.validateRule(this._schema[key], data[key], data)
  324. } catch (error) {
  325. result.push({
  326. key: key,
  327. errorMessage: error.message
  328. })
  329. if (!all) break
  330. }
  331. }
  332. return result
  333. }
  334. _checkField(data) {
  335. var keys = Object.keys(data)
  336. var keys2 = Object.keys(this._schema)
  337. if (new Set(keys.concat(keys2)).size === keys2.length) {
  338. return ''
  339. }
  340. return [{ key: 'invalid', errorMessage: SchemaValidator.message['defaultInvalid'] }]
  341. }
  342. }
  343. function Message() {
  344. return {
  345. default: '验证错误',
  346. defaultInvalid: '非法字段',
  347. required: '{label}必填',
  348. 'enum': '{label}超出范围',
  349. whitespace: '{label}不能为空',
  350. date: {
  351. format: '{label}日期{value}格式无效',
  352. parse: '{label}日期无法解析,{value}无效',
  353. invalid: '{label}日期{value}无效'
  354. },
  355. types: {
  356. string: '{label}类型无效',
  357. array: '{label}类型无效',
  358. object: '{label}类型无效',
  359. number: '{label}类型无效',
  360. date: '{label}类型无效',
  361. boolean: '{label}类型无效',
  362. integer: '{label}类型无效',
  363. float: '{label}类型无效',
  364. regexp: '{label}无效',
  365. email: '{label}类型无效',
  366. url: '{label}类型无效'
  367. },
  368. string: {
  369. len: '{label}必须为{length}个字符',
  370. min: '{label}不能少于{minLength}个字符',
  371. max: '{label}不能超过{maxLength}个字符',
  372. range: '{label}必须介于{minLength}和{maxLength}个字符之间'
  373. },
  374. number: {
  375. len: '{label}必须等于{length}',
  376. min: '{label}不能小于{minimum}',
  377. max: '{label}不能大于{maximum}',
  378. range: '{label}必须介于{minimum}and{maximum}之间'
  379. },
  380. pattern: {
  381. mismatch: '{label}格式不匹配'
  382. }
  383. };
  384. }
  385. SchemaValidator.message = new Message();
  386. export default SchemaValidator