{"id":29926661,"url":"https://github.com/ecomfe/san-validation","last_synced_at":"2025-10-03T22:12:17.574Z","repository":{"id":57158702,"uuid":"62486318","full_name":"ecomfe/san-validation","owner":"ecomfe","description":"Validation helpers on top of jsen","archived":false,"fork":false,"pushed_at":"2017-01-09T07:43:54.000Z","size":83,"stargazers_count":6,"open_issues_count":0,"forks_count":0,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-05-25T08:05:22.042Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ecomfe.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-07-03T07:40:15.000Z","updated_at":"2019-10-05T09:52:24.000Z","dependencies_parsed_at":"2022-09-07T21:02:23.190Z","dependency_job_id":null,"html_url":"https://github.com/ecomfe/san-validation","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/ecomfe/san-validation","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ecomfe%2Fsan-validation","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ecomfe%2Fsan-validation/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ecomfe%2Fsan-validation/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ecomfe%2Fsan-validation/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ecomfe","download_url":"https://codeload.github.com/ecomfe/san-validation/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ecomfe%2Fsan-validation/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268392180,"owners_count":24243297,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-02T02:00:12.353Z","response_time":74,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-08-02T12:42:29.679Z","updated_at":"2025-10-03T22:12:17.505Z","avatar_url":"https://github.com/ecomfe.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# san-validation\n\n`san-validation`提供了一系列简单的函数，基于[jsen](https://github.com/bugventure/jsen)实现对象的验证，验证使用JSON Schema作为对象描述，返回错误信息与jsen一致。\n\n本类库的`validation`模块（也即主模块）为一个函数，称之为`validation`函数，其签名如下：\n\n```\n{Function} validation({Object} schema, {Object | null} messages, {Object} [options])\n```\n\n该函数返回一个函数，用于对输入进行校验，校验返回一个对象，结构如下：\n\n```\n{\n    {boolean} isValid,\n    [Object[]] errors\n}\n```\n\n其中`errors`对象为jsen返回的结果，具体请查看jsen的相关API。\n\n普通的使用方法：\n\n```js\nimport validation from 'san-validation';\n\nlet validate = validation(mySchema);\n\nlet {isValid, errors} = validate(inputForm);\nif (!isValid) {\n    reportErrors(errors);\n}\n```\n\n## 消息分离\n\n在一些业务中，同样的JSON Schema会用于不同的视图界面，在不同的界面则要应用不同的错误信息。本类库支持将JSON Schema中的结构描述与错误信息进行分离，这么做也可以让schema更纯粹地描述一个对象的结构，而与校验有关的信息则在实际进行校验时再提供。\n\n使用`validation`函数的第二个参数可以对原有的schema添加错误信息，假设我们的原schema如下：\n\n```javascript\nlet schema = {\n   type: 'object',\n   description: 'product',\n   properties: {\n       categoryId: {\n           type: 'number',\n           description: 'category'\n       },\n       tags: {\n           type: 'array',\n           description: 'tags',\n           minItems: 1,\n           uniqueItems: true,\n           items: {\n               type: 'string',\n               description: 'tag of product',\n               maxLength: 30\n           }\n       },\n       sales: {\n           type: 'array',\n           items: {\n               type: 'object',\n               properties: {\n                   year: {\n                       type: 'number',\n                       description: 'sales year',\n                       minimum: 1900\n                   },\n                   month: {\n                       type: 'number',\n                       description: 'sales month',\n                       minimum: 1,\n                       maximum: 12\n                   },\n                   quantity: {\n                       type: 'number',\n                       description: 'sales quantity',\n                       minimum: 0\n                   }\n               }\n           }\n       }\n   },\n   required: ['categoryId', 'sales']\n}\n```\n\n对应的`message`对象如下：\n\n```javascript\nlet message = {\n    type: 'Invalid ${description} type',\n\n    categoryId: {\n        type: 'Invalid ${description} type'\n    },\n\n    tags: {\n        minItems: 'There should be no less than ${minItems} ${description}',\n        uniqueItems: 'Duplicate ${description}',\n\n        items: {\n            type: '${description} should be ${type}s',\n            maxLength: 'Each ${description} should have no more than ${maxLength} characters'\n        }\n    },\n\n    sales: {\n        items: {\n            year: {\n                minimum: '${description} is too early'\n            },\n\n            month: {\n                minimum: '${description} should fall within 1 - 12',\n                maximum: '${description} should fall within 1 - 12'\n            },\n\n            quantity: {\n                minimum: 'Need a positive ${description}'\n            }\n        }\n    }\n}\n```\n\n在`message`对象中，可以使用`${xxx}`占位符输出，占位符可用的内容与这个字段的schema中的属性对应，但**并不会向上或向下找父字段/子字段的相关属性**。\n\n`validation`函数会将`message`中的各个错误消息合并到schema中，大致的合并算法如下：\n\n1. 找到`invalidMessage`和`requiredMessage`直接与原schema合并，从`message`中移除这2个属性\n2. `message`的每个属性，如果值为字符串则是一个错误消息模板，如果是对象则为更进一步的属性-错误信息配置。\n3. 将同级别的`message`中错误信息提取出来，作为`messages`属性与原schema合并\n4. 将其它属性提取出来，如果原schema的`type`为`object`，则作为`properties`与原schema合并\n5. 如果原schema的`type`为`array`，则提取`items`属性与原schema合并\n\n因此基于上面的结构，我们可以进行校验：\n\n```javascript\nlet product = {\n    categoryId: 'invalid',\n\n    tags: [\n        'x'.repeat(50),\n        'x'.repeat(50),\n        123\n    ],\n\n    sales: [\n        {year: 1800, month: 14, quantity: -23}\n    ]\n};\n\nlet {isValid, errors} = validation(schema, message, {greedy: true})(product);\n```\n\n这样我们可以得到以下的错误`errors`对象：\n\n```javascript\n[\n    {path: 'categoryId', keyword: 'type', message: 'Invalid category type'},\n    {path: 'tags', keyword: 'uniqueItems', message: 'Duplicate tags'},\n    {path: 'tags.0', keyword: 'maxLength', message: 'Each tag of product should have no more than 30 characters'},\n    {path: 'tags.1', keyword: 'maxLength', message: 'Each tag of product should have no more than 30 characters'},\n    {path: 'tags.2', keyword: 'type', message: 'tag of product should be strings'},\n    {path: 'sales.0.year', keyword: 'minimum', message: 'sales year is too early'},\n    {path: 'sales.0.month', keyword: 'maximum', message: 'sales month should fall within 1 - 12'},\n    {path: 'sales.0.quantity', keyword: 'minimum', message: 'Need a positive sales quantity'},\n]\n```\n\n可以看到每一个错误对象都使用了`message`配置中对应的消息，对于数组则使用的`items`中配置的消息。\n\n## 默认错误信息\n\n大部分业务项目有一套默认的错误信息规则，如所有“必填”字段的提示均为“XXX字段为必填项”，我们不希望每次编写schema时都对这些字段一一指明错误信息，因此`san-validation`提供了支持默认错误信息的功能。\n\n`validation`模块的`withDefaultMessages`函数接收一个默认消息配置对象，返回一个新的`validation`函数。\n\n默认消息配置对象的每个键对应一个错误类型（如`type`、`minLength`、`maximum`等），值为一个函数，该函数接收当前字段的schema，需要返回错误信息字符串。\n\n我们通常推荐一个项目独立一个模块提供自己的`validation`函数，可在这个模块内配置默认错误信息，如编写一个`common/validation.js`文件，可以使用以下代码提供默认的错误信息：\n\n```javascript\n/**\n * @file common.validation.js\n */\n\nimport {withDefaultMessages} from 'san-validation';\n\nlet messages = {\n    type({description}) {\n        return `${description}的类型不符`;\n    },\n\n    minLength({description, minLength}) {\n        if (minLength === 1) {\n            return `必须输入${description}`;\n        }\n\n        return `${description}不得小于${minLength}个字符`;\n    },\n\n    // ...\n};\n\nexport default withDefaultMessages(messages);\n```\n\n其它模块则直接使用项目中的`validation`模块：\n\n```javascript\nimport validation from 'common/validation';\nimport schema from './schema';\n\n// 额外的错误信息\nlet errorMessages = {\n    roles: {\n        items: {\n            oneOf: '用户角色必须为“正常用户”、“VIP”、“管理员”之一'\n        }\n    }\n};\nlet validateUser = validation(schema, errorMessages, {greedy: true});\n\nexport default class Form {\n    validate(input) {\n        return validateUser(input);\n    }\n}\n```\n\n## 基于源码构建\n\n除了包目录下的`validation.js`和`validation.min.js`外，可以直接基于`src`目录下的源码进行构建，需要使用babel及`es2015`预设集。\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fecomfe%2Fsan-validation","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fecomfe%2Fsan-validation","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fecomfe%2Fsan-validation/lists"}