后端前端开发工具移动开发【免费下载链接】meteorMeteor, the JavaScript App Platform项目地址https://gitcode.com/gh_mirrors/me/meteor点击查看免费下载check是 Meteor 平台内置的轻量级参数校验与通用模式匹配包专门用于在Meteor.publish、Meteor.methods等入口对传入参数做严格的类型与结构校验防止任意对象如 MongoDB selector被当作参数传入。读完本文你将掌握check()的全部用法、Match命名空间下所有匹配模式的语义差异、底层匹配引擎的实现原理以及如何借助Match.Error、throwAllErrors和参数审计机制写出安全可靠的 Meteor 应用代码。check 包是什么一句话定位与适用场景根据 packages/check/README.md 的定义check是一个用于参数检查argument checking和通用模式匹配pattern matching的轻量级包。它的核心目标是在运行时断言一个值是否符合某个模式如果不符合立即抛出Match.Error。它在当前仓库中的实现位于 packages/check/match.js包描述为Check whether a value matches a pattern当前版本为 1.5.0见 packages/check/package.js。该包依赖ecmascript与ejson对外导出check和Match两个全局符号可在客户端与服务端同时使用locus: Anywhere。典型应用场景非常明确Method 方法入口校验客户端传来的参数类型与结构防止脏数据进入业务逻辑Publication 发布入口例如确保roomId是字符串而不是一个任意的 Mongo selector 对象避免订阅接口被滥用任何函数边界的防御式校验作为轻量级的运行时断言工具。快速上手来自官方 README 的典型用法原文档给出的示例完整展现了该包的两个经典使用场景这里原样继承并补充说明Meteor.publish(chats-in-room, function (roomId) { // 确保 roomId 是字符串而不是一个任意的 mongo selector 对象。 check(roomId, String); return Chats.find({room: roomId}); }); Meteor.methods({addChat: function (roomId, message) { check(roomId, String); check(message, { text: String, timestamp: Date, // 可选字段如果存在则必须是字符串数组。 tags: Match.Optional([String]) }); // ... do something with the message ... }});要点拆解check(roomId, String)断言roomId是原始字符串类型。若客户端传入{room: xxx}这类对象check会立即抛出Match.Error发布逻辑不会继续执行check(message, {...})断言message是一个普通对象其中text必须是字符串、timestamp必须是Date实例、tags若存在则必须是字符串数组Match.Optional([String])声明tags为可选字段——对象中缺少该键时不报错一旦出现则该值必须匹配[String]模式。这正是本包与手写if (typeof x ! string)的最大区别一个声明式模式即可表达对象 必填键 可选键 嵌套数组 元素类型的完整结构约束。check() 函数签名与行为check的函数签名定义在 packages/check/match.jsexport function check(value, pattern, options { throwAllErrors: false }) { // ... const result testSubtree(value, pattern, options.throwAllErrors); if (result) { if (options.throwAllErrors) { throw Array.isArray(result) ? result.map(r format(r)) : [format(result)] } else { throw format(result) } } }参数说明参数类型说明valueAny待校验的值patternMatchPattern匹配模式详见下文模式全集options.throwAllErrorsBoolean默认false遇到第一个错误立即抛出设为true时收集所有错误并一次性抛出错误数组行为要点匹配成功静默返回无副作用匹配失败抛出Match.Error默认模式错误对象上带有描述性的message与定位失败字段的paththrowAllErrors: true抛出的是一个Match.Error数组数组中每个元素对应一个独立的失败点与Match.test()不同check()会与内部参数审计机制见后文交互记录哪些参数已经被检查过。可用的匹配模式Match Patterns全集所有模式都通过Match命名空间定义于 packages/check/match.js或直接使用 JS 原生类型构造。下面按匹配语义逐一展开。基础类型String / Number / Boolean / Function / undefinedString、Number、Boolean、Function、undefined五种可以直接作为模式使用匹配规则是严格的typeof比较。注意两点模式匹配不包含装箱对象boxed objectsnew String(foo)不能匹配String模式源码注释 Do not match boxed objects测试见 packages/check/match_test.jsNaN、Infinity均属于Number类型可以匹配Number模式见 match_test.js。check(hello, String); // 通过 check(42, Number); // 通过 check(true, Boolean); // 通过 check(function(){}, Function); // 通过 check(undefined, undefined); // 通过 check(new String(x), String); // 抛出 Match.Errornull 与字面量匹配null作为模式时只匹配值为null的情况字符串、数字、布尔值作为模式时采用字面量精确匹配value pattern配合Match.OneOf可用于枚举白名单。相关实现见 match.js测试覆盖于 match_test.js。check(null, null); // 通过 check(asdf, asdf); // 通过 check(123, 123); // 通过 check(123, 123); // 抛出 Match.Error类型与值都不同Match.AnyMatch.Any匹配任意值包括undefined、null、函数等一切内容。它在源码中实现为一个特殊的数组标记[__any__]在 testSubtree 中首先被短路判断if (pattern Match.Any) { return false; // 表示匹配成功 }Match.IntegerMatch.Integer只匹配有符号 32 位整数signed 32-bit integers即满足(value | 0) value的 number。源码在 match.js 中给出了选择位运算方案的原因JS 中判断整数常用的取余数方案在超大浮点数如1.348192308491824e23上会误判为整数而位运算虽然会把数值强制转换为 32 位有符号整数但行为是确定且一致的。check(-1, Match.Integer); // 通过 check(2147483647, Match.Integer); // 通过INT_MAX check(123.33, Match.Integer); // 失败 check(NaN, Match.Integer); // 失败 check(Infinity, Match.Integer); // 失败 check(1.348192308491824e23, Match.Integer); // 失败32 位溢出场景边界行为均已被 match_test.js 验证-2147483648INT_MIN与2147483647INT_MAX通过NaN、±Infinity、对象、数组、函数、Date实例全部失败。Match.NonEmptyStringMatch.NonEmptyString匹配非空字符串。它在源码内部被转换为一个Match.Where条件match.js该条件先check(value, String)再要求value.length 0function nonEmptyStringCondition(value) { check(value, String); return value.length 0; }check(xx, Match.NonEmptyString); // 通过 check(, Match.NonEmptyString); // 失败数组模式[pattern]单个元素组成的数组[pattern]表示同构数组要求值本身是数组或类数组的arguments对象且每个元素都匹配pattern。源码处理见 match.js。注意模式数组必须恰好一个元素否则报Bad pattern: arrays must have one type element空数组[]匹配任何[pattern]matches([], [Number])通过match_test.js不支持异构数组这是源码注释中明确列出的不支持项Things we explicitly do NOT support: heterogenous arraysarguments对象会被当作数组处理通过isArguments判断见 match.js相关测试见 match_test.js数组模式可以嵌套[[[String]]]表示三维字符串数组。check([1, 2, 3], [Number]); // 通过 check([], [Number]); // 通过 check([1, 4], [Number]); // 失败 check([1, 2, [3]], [Number]); // 失败嵌套数组元素 check(arguments, [Number]); // 通过函数内对象模式{key: pattern}直接传入一个普通对象字面量作为模式表示对对象的结构校验每个键对应一个子模式未用Match.Optional/Match.Maybe包裹的键为必填键缺失时报Missing key xxx而模式中未声明的键则一律报Unknown key错误不允许有多余键。check({a: 1, b: 2}, {b: Number, a: Number}); // 通过键顺序无关 check({a: 1, b: 2}, {b: Number}); // 失败Unknown key: a check({}, {a: Number}); // 失败Missing key a check({foo: 42}, {}); // 失败Unknown key: foo更重要的限制是对象模式只匹配普通对象plain object。源码在 match.js 中通过isPlainObject判断而该实现是 jQuery 3.1.1isPlainObject的服务端复制版见 packages/check/isPlainObject.js。因此类实例new F()即使结构完全一致也不匹配对象模式match_test.jsDate、RegExp等内置对象不匹配{}通过Object.create(parentObj)继承得到的对象不匹配match_test.js注意Object作为模式是Match.ObjectIncluding({})的简写即任意普通对象match.js。Match.ObjectIncludingMatch.ObjectIncluding(pattern)表示部分匹配模式中声明的键必须存在且匹配但允许值对象带有额外的未知键。源码实现将unknownKeysAllowed置为truematch.js。check({a: 1, b: 2}, Match.ObjectIncluding({b: Number})); // 通过a 被忽略 check({a: 1, b: 2}, Match.ObjectIncluding({c: String})); // 失败Missing key c当需要只要某个字段存在且合法时非常有用例如校验 MongoDB 文档的局部字段。Match.ObjectWithValuesMatch.ObjectWithValues(pattern)匹配键任意、但所有值都必须匹配 pattern的对象。源码将其unknownKeysAllowed置为true且设置unknownKeyPatternpattern本身被替换为空对象无必填键见 match.js。check({}, Match.ObjectWithValues(Number)); // 通过空对象 check({x: 1, y: 2}, Match.ObjectWithValues(Number)); // 通过 check({x: 1, y: 2}, Match.ObjectWithValues(Number)); // 失败y 不是数字Match.Optional 与 Match.Maybe这是最容易混淆的一对模式二者语义存在微妙但关键的区别match.js 展示了它们的展开逻辑模式顶层语义对象中语义Match.Optional(p)匹配undefined或p不接受null键可缺失若键存在其值不能是undefined/null除非p本身允许Match.Maybe(p)匹配undefined、null或p与Match.Optional在对象中的行为一致实现上Optional被展开为Match.OneOf(undefined, p)Maybe被展开为Match.OneOf(undefined, null, p)。测试中的关键断言match_test.jscheck(null, Match.Optional(String)); // 失败Optional 不接受 null check(undefined, Match.Optional(String)); // 通过 check(null, Match.Maybe(String)); // 通过 check({}, {a: Match.Optional(Number)}); // 通过键缺失 check({a: undefined}, {a: Match.Optional(Number)}); // 失败键存在但值为 undefined check({a: undefined}, {a: Match.Maybe(Number)}); // 失败对象内行为同 Optional设计动机在 packages/check/check.d.ts 的类型注释中说明undefined参数通过 DDP 传输到服务端后会被转换成null因此对于客户端可能不传该参数的字段服务端需要使用Match.Maybe才能同时容忍缺省undefined与空值null。Match.OneOfMatch.OneOf(...patterns)匹配至少命中一个子模式的情况常与字面量、null、Boolean组合实现枚举或联合类型校验check(42, Match.OneOf(asc, desc, 42)); // 通过 check(foo, Match.OneOf(String, Number)); // 通过 check(3, Match.OneOf(null, Boolean)); // 失败3 不是 null 也不是布尔注意构造约束OneOf至少要提供一个选项否则抛Must provide at least one choice to Match.OneOfmatch.js。此外Optional与Maybe本质上都是OneOf的特殊形式因此OneOf的失败错误消息中会同时提及三者Failed Match.OneOf, Match.Maybe or Match.Optional validation。Match.WhereMatch.Where(condition)允许传入自定义谓词函数进行任意校验。谓词有两种通过方式match.js返回true内部调用check()成功即不抛错。若谓词返回false或抛出Match.Error则匹配失败若抛出其他类型错误该错误会原样向外抛出不会吞掉。check(42, Match.Where(x x % 2 0)); // 通过 check(43, Match.Where(x x % 2 0)); // 失败 check({}, Match.Where(EJSON.isBinary)); // 失败 // 谓词内部复用 check 的组合式写法 check(abc, Match.Where(x { check(x, String); // 类型断言失败会以 Match.Error 形式向外传播 return x.length 1; }));构造函数模式instanceof任意函数非上述特例都会被当作构造函数处理使用instanceof判断match.js。因此Date、RegExp乃至自定义类都可以作为模式check(new Date, Date); // 通过 check(/foo/, RegExp); // 通过 check(new Date, Number); // 失败Match.OneOf(Number, String)与自定义构造函数如F的组合也能正确工作测试覆盖于 match_test.js。Match.test不抛异常的布尔校验当只需要判断而不抛错时使用Match.test(value, pattern)。它与check()的区别match.js仅返回true/false不参与参数审计机制不会记录参数已被检查不会把错误转换为Meteor.Error若匹配过程中抛出Match.Error以外的错误会向上抛出。if (Match.test(roomId, String)) { // ... }在 match_test.js 中测试辅助函数同时断言check不抛错 ⇔Match.test返回 true、check抛Match.Error⇔Match.test返回 false说明二者共享同一套testSubtree匹配引擎语义完全一致。解析 Match.Error消息、路径与 DDP 场景下的处理Match.Error是通过Meteor.makeErrorType创建的错误类型match.js具有三个关键属性message以Match error:为前缀内容形如Expected string, got number、Missing key bar、Expected Integer, got 3.14path定位失败字段的路径字符串从空字符串开始随错误在递归中回溯逐级拼接形如foo[1].bar、[0].$FoO[bar baz\n\]、$set.peoplesanitizedError一个Meteor.Error(400, Match failed)。这是为 DDP 场景设计的——当错误跨网络传输到客户端时不会泄露服务端内部细节而是给出一个干净且比500 Internal server error更有意义的 400 错误。错误路径的格式化规则由_prependPath实现match.js数字索引使用方括号[i]合法标识符使用点号.key包含空格、引号、$或 JS 关键字如return的键会被 JSON 序列化后放入方括号。测试中的断言示例match_test.jscheck({foo: [{bar: 3}, {bar: something}]}, {foo: [{bar: Number}]}) // 失败路径foo[1].bar // 失败消息Match error: Expected number, got string in field foo[1].barthrowAllErrors一次性收集全部校验错误默认情况下check在第一个错误处立即抛出fail-fast。传入{ throwAllErrors: true }后testSubtree会以collectErrors模式遍历整个值结构收集所有失败点并以Match.Error数组形式抛出match.js。该模式在 match_test.js 中有一个深度嵌套的综合用例一个包含text、emails、things、stuff、maybe、opt、int、oneOf、where、embedded等十余个字段的复杂对象在多种模式约束下一次性收集到 40 个Match.Error并精确断言了其中的缺键错误try { check(value, pattern, {throwAllErrors: true}); } catch (e) { // e 是一个 Match.Error 数组例如 // [ // Match error: Missing key another in field embedded, // Match error: Missing key missing1, // Match error: Missing key missing2, // ... // ] }适用场景表单提交、批量数据处理等希望一次告诉调用方所有问题的校验需求而交互式 Method 调用通常更适合默认的 fail-fast 行为。源码探秘testSubtree 递归匹配引擎所有模式匹配的最终执行者是内部函数testSubtree(value, pattern, collectErrors, errors, path)match.js。它的工作方式值得关注短路优先先判断Match.Any再遍历typeofChecks表String/Number/Boolean/Function/undefined与typeof结果的映射表match.js完成基础类型匹配类型分派按null→ 字面量 →Match.Integer→ 数组 →Where→Maybe/Optional先展开为OneOf→OneOf→ 构造函数 → 对象模式的顺序逐层分派。注意数组分支必须在Match.Any之后判断因为Match.Any自身就是以数组形式编码的对象模式两阶段先扫描值的每个键必填键校验、可选键校验、未知键拒绝再检查剩余未消费的必填键并生成Missing key错误错误收集collectErrors为 true 时结果对象携带完整path并入数组最终返回整个错误数组否则任一失败立即返回。对象模式只接受普通对象这一约束值得再次强调——源码在 match.js 中显式报错Expected plain object其背后的isPlainObject严格检查由全局Object构造函数创建的、或原型为 null 的对象packages/check/isPlainObject.js。参数审计_failIfArgumentsAreNotAllChecked 与 audit-argument-checkscheck包还提供了一套强制所有参数都被检查的审计机制Match._failIfArgumentsAreNotAllChecked(f, context, args, description)match.js。其工作原理将args做浅拷贝后压入一个ArgumentChecker栈结构见 match.js通过currentArgumentChecker一个Meteor.EnvironmentVariable在执行函数f期间挂载该检查器每次调用check()时被检查的值会从参数栈中注销函数执行完毕后若仍有参数未被任何check()覆盖抛出Did not check() all arguments during ${description}。这套机制在 DDP 服务端被实际使用packages/ddp-server/livedata_server.js 中的maybeAuditArgumentChecks会在audit-argument-checks包存在时用Match._failIfArgumentsAreNotAllChecked包裹 Method 与 Publish 的处理函数从而在开发阶段强制开发者对每个传入参数都做check()。这正是 packages/audit-argument-checks 包存在的意义——它本身不含业务代码仅通过弱依赖关系激活这套审计。测试覆盖见 match_test.js其中甚至验证了 NaN 参数、check(args, [Number])批量检查等边界场景。在 Meteor 生态中的真实应用check不只是独立工具包它被 Meteor 自身的核心包广泛使用以下是仓库内的几个真实落点packages/allow-deny/allow-deny.jsallow/deny方法包装层使用check(arguments, [Match.Any])统一声明所有参数都已被有意处理packages/allow-deny/allow-deny.js_validatedUpdateAsync中check(mutator, Object)校验更新操作符必须是普通对象packages/ddp-server/livedata_server.js如上所述Method/Publish 调用的参数审计入口packages/accounts-base/accounts_server.js、packages/accounts-password/password_server.js、packages/constraint-solver/solver.js 等均有大量Match.*模式用于校验内部数据结构如用户名、邮箱、密码选项。这些调用印证了check在 Meteor 中的定位它是框架自身也在依赖的运行时类型系统而不仅仅是面向应用开发者的工具。TypeScript 支持check.d.ts该包同时提供完整的 TypeScript 类型声明 packages/check/check.d.ts作为资产打包在服务端核心是两个高级类型Match.Pattern递归定义所有合法模式类型——typeof String/Number/Boolean/Object/Function、构造函数、undefined | null | string | number | boolean、[Pattern]、{[key: string]: Pattern}以及实现了MatcherT接口的Match.*结果Match.PatternMatchT把模式类型映射为对应的值类型例如typeof String → string、[Pattern] → PatternMatchT[]、对象模式 → 同构对象类型。基于此check被声明为类型守卫type guard / assertion functionexport declare function checkT extends Match.Pattern( value: any, pattern: T, options?: { throwAllErrors?: boolean } ): asserts value is Match.PatternMatchT;也就是说在 TypeScript 项目中执行check(x, String)之后编译器会自动把x收窄为string实现一次校验运行期与编译期双重保障。Match.test同样声明了value is PatternMatchT的类型谓词。测试验证match_test.js 关键用例完整的测试套件位于 packages/check/match_test.js通过 packages/check/package.js 注册到 client 与 server 两端运行主要覆盖测试组覆盖内容check - check全部基础类型、数组、对象、Optional/Maybe/OneOf/Where/Integer/构造函数/非普通对象/arguments等全量匹配与失败断言check - check throw all errorsthrowAllErrors: true模式下同样的全量用例check - check throw all errors deeply nested深度嵌套结构一次性收集 40 个错误的场景check - argument checker_failIfArgumentsAreNotAllChecked的全部参数都被检查与未检查完则报错行为check - Match error path错误路径格式化数组下标、$、空白、引号、JS 关键字键名check - Match error message错误消息文案的精确断言Expected string, got number等check - Match methods ... constructors保证new Match.Optional()等构造函数式调用可用保持向后兼容这套测试既是行为规范也是学习check语义边界的绝佳参考例如Optional不接受null、Maybe接受null、{a: undefined}不通过{a: Match.Maybe(Number)}等微妙规则都能在测试中找到明文断言。最佳实践小结在每个 Method / Publish 入口的第一行就check参数在业务逻辑接触数据之前拦截非法输入能用声明式模式就用手写判断check(message, {...})的可读性与可维护性远胜一串if/else正确区分Optional与Maybe仅在接受缺省时用Optional当字段可能以null形式经 DDP 传输时使用Maybe对象字面量模式默认拒绝未知键这有助于收紧接口契约需要宽容时显式使用Match.ObjectIncluding用Match.Where表达无法用类型组合描述的业务规则并在谓词内复用check以获得精确的错误信息开发期引入audit-argument-checks包强制自己检查每一个参数避免漏检结合Match.test做条件判断只在真正需要失败即抛错的边界处使用check在 TypeScript 项目中使用check的类型守卫能力让运行时校验同时完成编译期类型收窄。check是 Meteor 体系中一个体积小、价值大的基础设施包它把参数校验从零散的样板代码提升为一种可组合、可递归、可审计的声明式能力并在框架自身的 DDP、权限校验与账户系统中被反复验证。掌握了它你就掌握了 Meteor 应用的第一道安全防线。赞分享后端前端开发工具移动开发【免费下载链接】meteorMeteor, the JavaScript App Platform项目地址https://gitcode.com/gh_mirrors/me/meteor点击查看免费下载相关推荐Meteor check 与 Match运行时类型校验与模式匹配完全指南Meteor check 与 Match运行时类型校验与模式匹配完全指南 check 是 Meteor 官方提供的轻量级运行时类型校验库它通过一套可扩展的后端前端开发工具移动开发Meteor check 包完全指南用 Match Patterns 实现类型与结构校验Meteor check 包完全指南用 Match Patterns 实现类型与结构校验 check 是 Meteor 内置的轻量级类型检查与模式匹配库用于后端前端开发工具移动开发Meteor audit-argument-checks 包详解用 check 强制方法参数校验杜绝不安全输入Meteor audit argument checks 包详解用 check 强制方法参数校验杜绝不安全输入 audit argument checks后端前端开发工具移动开发创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考