尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Mongoose 自定义类型转换(Custom Casting)完全指南:用 SchemaType.cast() 覆盖内置 Casting

发布时间:2026/9/10 21:57:20

资讯中心
01
ARTICLE

Mongoose 自定义类型转换(Custom Casting)完全指南:用 SchemaType.cast() 覆盖内置 Casting

Mongoose 自定义类型转换(Custom Casting)完全指南:用 SchemaType.cast() 覆盖内置 Casting
Mongoose 自定义类型转换Custom Casting完全指南用 SchemaType.cast() 覆盖内置 Casting【免费下载链接】mongooseMongoDB object modeling designed to work in an asynchronous environment.项目地址: https://gitcode.com/GitHub_Trending/mo/mongoose本指南讲解 Mongoose 的**自定义类型转换Custom Casting**机制——通过SchemaType.cast()全局覆盖某个 SchemaType 的内置转换函数从而让类型转换规则完全贴合业务需求。文中以“把日文数字字符串二转成数字2”为主线案例并深入 lib/schemaType.js、lib/schema/number.js、lib/cast/number.js 等源码剖析自定义转换的底层调用链与错误包装机制。读完你将掌握Mongoose 内置转换何时会失败、如何无侵入地覆盖默认转换、如何委托原函数、如何彻底禁用转换以及如何在全局与单个 SchemaType 实例两个层面定制转换行为。什么是 Casting为什么需要自定义在 Mongoose 中**Casting类型转换**指把传入的任意值如 HTTP 请求里的字符串、表单提交的文本转换成 Schema 中声明的目标类型的过程。例如 Schema 声明age: Number时Mongoose 会在赋值、校验、查询时尝试把值转换为数字。Mongoose 5.4.0 起引入了若干种全局配置 SchemaType的能力其中就包括SchemaType.cast()这个函数它允许开发者覆盖 Mongoose 内置的转换逻辑。内置转换是“尽力而为”的它覆盖了字符串数字、布尔值、包装对象等常见情况但无法覆盖所有业务场景。例如默认情况下 Mongoose 无法把包含日文数字的字符串二转换为数字会直接抛出转换失败错误CastError。默认行为日文数字触发 CastError先看默认行为。声明age: Number后给文档赋值为字符串二并执行同步校验const schema new mongoose.Schema({ age: Number }); const Model mongoose.model(Test, schema); const doc new Model({ age: 二 }); const err doc.validateSync(); // Cast to Number failed for value 二 (type string) at path age err.message;validateSync()返回的err.message会包含类似Cast to Number failed for value 二 (type string) at path age的信息。也就是说字符串二无法被内置的数字转换函数接受校验失败。这条行为同样被仓库测试锁定test/docs/custom-casting.test.js 中的casting error用例断言了错误信息中必须包含Cast to Number failed for value 二 (type string) at path age。全局覆盖mongoose.Number.cast(fn)SchemaType的子类如Number上都挂载了静态的cast()方法它同时承担“读取”与“写入”两个职责无参数调用mongoose.Number.cast()返回当前生效的转换函数即原来的内置转换。传入函数调用mongoose.Number.cast(fn)设置新的转换函数此后所有 Number 路径的转换都会走fn。于是可以这样为数字类型扩展“日文数字”支持// 先保存当前内置的转换函数 const originalCast mongoose.Number.cast(); // 用自定义函数覆盖遇到 二 返回 2其余委托给内置转换 mongoose.Number.cast(v { if (v 二) { return 2; } return originalCast(v); }); const schema new mongoose.Schema({ age: Number }); const Model mongoose.model(Test, schema); const doc new Model({ age: 二 }); const err doc.validateSync(); err; // null —— 校验通过 doc.age; // 2 —— 已被转换为数字关键点有两个先取出原函数再委托const originalCast mongoose.Number.cast()拿到内置转换自定义函数对无法识别的值调用originalCast(v)走原有逻辑避免破坏默认行为。覆盖是全局的该设置在mongoose实例或mongoose单例层面生效之后新建的所有 Schema 中的 Number 路径都会使用新转换。该用例在 test/docs/custom-casting.test.js 中由casting override测试验证断言err为null且doc.age 2。彻底禁用转换cast(false) 与严格模式除了传入自定义函数cast()还支持传入false来完全关闭该类型的转换。在 lib/schema/number.js 中SchemaNumber.cast function cast(caster) { if (arguments.length 0) { return this._cast; } if (caster false) { caster this._defaultCaster; } this._cast caster; return this._cast; };传入false时会回退到_defaultCaster——一个只接受number类型、其余一律抛错的严格函数SchemaNumber._defaultCaster v { if (typeof v ! number) { throw new Error(); } return v; };也就是说// 禁用 Number 的一切隐式转换 mongoose.Number.cast(false); // 之后即使传 123 这样的纯数字字符串也会抛错只有真正的 number 才能通过这一点在 lib/schemaType.js 的基类实现里略有不同基类的false回退为v v恒等函数原样返回而 Number 的false回退为只接受number的严格校验器。因此“禁用转换”的语义因类型而异使用前应结合目标类型的实现确认。官方文档注释也给出了这一用法见 lib/schema/number.jsmongoose.Number.cast(false)等价于“完全禁用转换”与之对应还可以用mongoose.Number.cast(v { if (v ) { return 0; } return original(v); })让空字符串转换为0。实例级定制castFunction()上面的cast()是类构造器级别的影响该类型的所有路径。若只想影响某一个 SchemaType 实例某一条具体路径可以使用SchemaType.prototype.castFunction()lib/schemaType.jsconst number new mongoose.Number(mypath, {}); number.castFunction(v { // 只影响 mypath 这条路径只允许 number 或 undefined assert.ok(v undefined || typeof v number); return v; });castFunction(caster, message)同样支持无参读取、传false回退到默认、传字符串设置自定义错误消息。在 lib/schema/number.js 的实例cast()方法中可以看到两者的优先级优先使用实例级this._castFunction其次回退到构造器级this.constructor.cast()返回的全局转换函数。这意味着你可以先用mongoose.Number.cast()做全局兜底再对个别路径用castFunction()做细粒度覆盖。底层原理转换失败如何变成 CastError自定义转换的执行链路可以从 lib/schema/number.js 的实例cast()方法看清SchemaNumber.prototype.cast function(value, doc, init, prev, options) { // 1. 处理引用populate ref与文档对象取 value._id if (typeof value ! number SchemaType._isRef(this, value, doc, init)) { if (value null || utils.isNonBuiltinObject(value)) { return this._castRef(value, doc, init, options); } } const val value?._id ! undefined ? value._id : value; // 2. 选择转换函数实例级优先其次构造器级 let castNumber; if (typeof this._castFunction function) { castNumber this._castFunction; } else if (typeof this.constructor.cast function) { castNumber this.constructor.cast(); } else { castNumber SchemaNumber.cast(); } // 3. 执行转换抛出的任何错误统一包装为 CastError try { return castNumber(val); } catch (err) { throw new CastError(Number, val, this.path, err, this); } };由此可以看出转换函数的选择顺序是实例级_castFunction→ 构造器级cast()→ 兜底SchemaNumber.cast()自定义函数中throw的任何错误都会被捕获并重新包装为CastError(Number, ...)与 Mongoose 内置的报错风格保持一致对_id字段会先取出value._id再转换兼容传入文档对象的情况。内置的默认转换函数实现在 lib/cast/number.js其完整规则为输入值转换结果null/undefined原样返回视为合法空字符串返回null字符串 / 布尔值Number(val)转换NaN结果抛出Cast to Number failed: value is not a valid numberNumber包装对象返回valueOf()普通number直接返回带valueOf函数的对象Number(val.valueOf())带toString且能转成数字的对象返回Number(val)其余情况抛出Cast to Number failed: value is not a valid number这正是自定义转换“委托原函数”时的行为基准。不止 Number其他 SchemaType 同样支持cast()是SchemaType基类的能力因此String、Date、Boolean、ObjectId、Decimal128、Double、Int32等内置类型也都支持全局自定义转换。仓库测试给出了多处佐证test/schematype.cast.test.js对应 issue gh-7045覆盖了ObjectId、Boolean等类型的自定义转换例如对ObjectId自定义转换让字符串special映射到合法的 ObjectId同时保持基类Schema.ObjectId的行为不变通过子类继承实现隔离class CustomObjectId extends Schema.ObjectId {} CustomObjectId.cast(v { if (v special) { return original.objectid(0.repeat(24)); } return original.objectid(v); });同一文件还验证了Schema.ObjectId.cast(false)后000000000000000000000000这类字符串会抛出CastError只有真正的ObjectId实例能通过test/schematype.cast.test.js。test/double.test.js 与 test/int32.test.js 分别演示了Double.cast(fn)与Int32.cast(fn)的覆盖写法。test/schematype.test.js 展示了在 SchemaType 实例上直接调用schemaType.cast(...)的用法。这些测试都遵循同一模式先在beforeEach中保存原转换函数在afterEach中恢复避免测试间相互污染。使用注意事项全局覆盖影响所有 Schemamongoose.Number.cast(fn)一旦设置后续创建的所有使用Number的路径都会受影响已有 Schema 实例在创建时已捕获转换函数行为视具体版本而定。因此务必保存并委托原函数并在不再需要时恢复例如const originalCast mongoose.Number.cast(); mongoose.Number.cast(v /* 自定义逻辑 */); // ...业务代码... mongoose.Number.cast(originalCast); // 恢复错误会被包装成 CastError自定义函数里throw new Error(...)会在外层被包装为带路径信息的CastError见 lib/schema/number.js调用方可通过err.name CastError判断。禁用转换语义因类型而异cast(false)在基类回退为恒等函数在Number回退为“仅接受 number”的严格函数使用前应查看对应类型源码确认。实例级覆盖更安全如果只想影响单条路径优先使用SchemaType.prototype.castFunction()lib/schemaType.js避免污染全局。与校验、查询联动自定义转换同时作用于文档赋值/校验和查询条件查询走castForQuery见 lib/schema/number.js因此转换函数需要能够处理来自查询过滤器的值。延伸阅读教程原文docs/tutorials/custom-casting.md基类实现lib/schemaType.js静态cast、实例castFunction、原型castNumber 实现lib/schema/number.js构造器级cast与_defaultCaster、lib/schema/number.js实例cast与 CastError 包装内置转换函数lib/cast/number.js、lib/cast/string.js、lib/cast/boolean.js类型注册lib/mongoose.jsmongoose.Number SchemaTypes.Number测试用例test/docs/custom-casting.test.js、test/schematype.cast.test.js【免费下载链接】mongooseMongoDB object modeling designed to work in an asynchronous environment.项目地址: https://gitcode.com/GitHub_Trending/mo/mongoose创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。