如何使用Javascript基于自定义Schema验证JSON请求的合法性
JSON请求Schema校验优化方案
方案一:原生JavaScript实现(无第三方依赖)
核心思路
把所有字段的校验规则抽成独立的Schema配置对象,通过递归迭代自动完成所有字段的必填、类型校验,不需要手动编写大量if判断,天然支持多层嵌套、数组结构的校验。
实现代码
首先定义和你业务匹配的校验Schema:
// 校验规则配置,新增/修改字段规则只需调整该对象 const validationSchema = { application: { type: 'object', required: true, properties: { partnerReferral: { type: 'object', required: true, properties: { partnerID: { type: 'string', required: true }, merchantReferenceID: { type: 'string', required: true } } }, businessInformation: { type: 'object', required: true, properties: { identity: { type: 'array', required: true, items: { type: 'object', properties: { identifier: { type: 'string', required: true }, value: { type: 'string', required: true } } } }, businessType: { type: 'string', required: true }, classification: { type: 'object', required: true, properties: { code: { type: 'string', required: true }, value: { type: 'string', required: true } } } } } } } }
然后编写通用递归校验逻辑:
// 错误模板配置 const ERROR_TEMPLATES = { REQUIRED_FIELD: (fieldPath) => `${fieldPath} 是必填字段,但请求中未提供`, TYPE_MISMATCH: (fieldPath, actualType, expectedType) => `${fieldPath} 预期数据类型为 ${expectedType},实际收到的是 ${actualType}` } /** * 递归校验方法 * @param {any} obj 待校验的请求字段值 * @param {object} schema 对应字段的校验规则 * @param {string} path 当前字段的完整路径,用于错误提示 */ function validate(obj, schema, path = '') { // 必填字段为空校验 if (schema.required && (obj == null || obj === '')) { return { valid: false, error: ERROR_TEMPLATES.REQUIRED_FIELD(path || '根请求对象') } } // 非必填字段为空直接跳过后续校验 if (!schema.required && (obj == null || obj === '')) { return { valid: true } } // 数据类型校验,单独区分数组类型 const actualType = Array.isArray(obj) ? 'array' : typeof obj if (actualType !== schema.type) { return { valid: false, error: ERROR_TEMPLATES.TYPE_MISMATCH(path || '根请求对象', actualType, schema.type) } } // 对象类型递归校验所有子属性 if (schema.type === 'object' && schema.properties) { for (const [prop, subSchema] of Object.entries(schema.properties)) { const currentPath = path ? `${path}.${prop}` : prop const validateRes = validate(obj[prop], subSchema, currentPath) if (!validateRes.valid) return validateRes } } // 数组类型递归校验每一个元素 if (schema.type === 'array' && schema.items) { for (let i = 0; i < obj.length; i++) { const currentPath = `${path}[${i}]` const validateRes = validate(obj[i], schema.items, currentPath) if (!validateRes.valid) return validateRes } } return { valid: true } } // 封装为你需要的checkRequest接口 function checkRequest(requestBody) { const defaultResponse = { validRequest: true, responseStatus: "", errorTitle: "", errorDetail: "", errorCode: "", } const validateRes = validate(requestBody, validationSchema) if (!validateRes.valid) { return { ...defaultResponse, validRequest: false, responseStatus: "400", errorCode: "40001", errorTitle: "Bad Request", errorDetail: validateRes.error } } return defaultResponse }
使用示例
const requestBody = { application: { partnerReferral: { partnerID: "mg3e09f8-a8dd-44e6-bb06-55293b799318", merchantReferenceID: "mg3e09f8a8dd44e6bb06-55293b799318" }, businessInformation: { identity: [ { "identifier": "EMPLOYER_IDENTIFICATION_NUMBER", "value": "77-1122333" } ], businessType: "ASSOCIATION", classification: { "code": "SIC", "value": "string" } } } } console.log(checkRequest(requestBody))
方案二:第三方库实现(推荐生产环境使用)
如果Workato环境允许引入第三方依赖,推荐使用Ajv,这是JS生态中性能最高、兼容性最好的JSON Schema校验库,支持所有标准JSON Schema特性,不需要自己维护校验逻辑。
实现示例
import Ajv from 'ajv' const ajv = new Ajv() // 按标准JSON Schema规范编写校验规则 const schema = { type: "object", required: ["application"], properties: { application: { type: "object", required: ["partnerReferral", "businessInformation"], properties: { partnerReferral: { type: "object", required: ["partnerID", "merchantReferenceID"], properties: { partnerID: { type: "string" }, merchantReferenceID: { type: "string" } } } // 剩余字段按相同规则补充即可 } } } } const validate = ajv.compile(schema) function checkRequest(requestBody) { const defaultResponse = { validRequest: true, responseStatus: "", errorTitle: "", errorDetail: "", errorCode: "", } const isValid = validate(requestBody) if (!isValid) { const error = validate.errors[0] return { ...defaultResponse, validRequest: false, responseStatus: "400", errorCode: "40001", errorTitle: "Bad Request", errorDetail: `${error.instancePath.slice(1)} ${error.message}` } } return defaultResponse }
原有代码优化点说明
你原有代码中存在重复逻辑导致的笔误:校验merchant_reference_id字段时,错误提示的字段名仍然写的是partnerID,使用规则配置+自动校验的方式可以完全避免这类问题。
内容的提问来源于stack exchange,提问作者Pstivala7
相关产品推荐
相关产品推荐

