Sails 应用内省指南:深入解析 `sails.getActions()` 方法

发布时间:2026/9/20 21:16:50

Sails 应用内省指南:深入解析 `sails.getActions()` 方法 Sails 应用内省指南深入解析sails.getActions()方法【免费下载链接】sailsRealtime MVC Framework for Node.js项目地址: https://gitcode.com/gh_mirrors/sa/sails导读sails.getActions()是 SailsRealtime MVC Framework for Node.js提供的公开 API 之一用于返回当前应用中已注册的全部 Action 的扁平字典。无论是传统控制器Controller中的动作方法还是使用 actions2 语法定义的独立 Action 文件甚至是各 Hook如 blueprints Hook动态注册的动作都会汇聚到这张字典中。读完本文你将掌握sails.getActions()的用法、返回结构的约定、底层实现原理以及它在 Hook 开发、路由绑定与运行时自省中的实际应用。sails.getActions()是什么getActions()是挂载在sails应用实例上的公开方法对应源码 lib/app/get-actions.js其作用是将 Sails 内部维护的actions 字典以浅克隆shallow clone的形式返回给调用方。它的用法极其简单sails.getActions();根据官方文档 sails.getActions() 的说明返回结果是一个扁平单层字典键keykebab-case短横线分隔风格的 action 身份标识identity值value对应的 action 函数。返回结构扁平字典与 Action 身份标识字典是扁平的所谓扁平指的是返回结果只有一层键值对不会嵌套。无论 Action 定义在哪个目录层级最终都以identity → function的形式平铺在同级字典中。源码注释给出了直观示例sails.getActions(); // // { // duck/quack: {...}, // // ... // }注意上面示例中的 identity 为duck/quack——这里虽然包含了/分隔符用于表达命名空间/目录层级但字典本身依然是单层结构。身份标识的命名约定Action identity 遵循 kebab-case 约定。在源码 lib/app/private/controller/help-register-action.js 中身份标识需要满足如下正则约束var IS_VALID_ACTION_IDENTITY_RX /^[a-z_\$][a-z0-9-_.\$]*(\/[a-z_\$][a-z0-9-_\$.]*)*$/;也就是说一个合法的 identity 由小写字母、数字、短横线、点、下划线、$组成可以用/划分多级命名空间。这也解释了为什么 Action 文件名必须是小写 kebab-case例如do-stuff.js而传统控制器文件名则要求 PascalCase 并以Controller结尾。所有 Action 都已被转换为req, res函数文档中特别强调了一点返回字典中的所有 Action 此时都已被转换为标准的(req, res)函数即使它们最初是使用 actions2 语法machine 定义编写的。这一保证的底层实现位于 help-register-action.js如果传入的 Action 本身就是函数则直接存入sails._actions字典如果传入的是 actions2 机器定义对象则通过machine-as-action包转换为可调用的(req, res)函数后再存入if (_.isFunction(action)) { actions[identity] action; } else { try { actions[identity] machineAsAction(_.extend({ implementationSniffingTactic: sails.config.implementationSniffingTactic||undefined, }, action)); } catch (e) { // ... 抛出 code E_INVALID 的错误 } }因此在调用sails.getActions()后你可以放心地对返回值中的每个 Action 直接做函数式检查或调用例如typeof action function。底层实现浅克隆保护内部字典getActions()的完整实现只有寥寥数行核心逻辑非常清晰module.exports function getActions() { // Return a shallow clone of the actions dictionary, so that the caller // cant modify the actions. return _.clone(this._actions); };有两个关键点值得展开数据来源是sails._actions这是 Sails 应用实例上保存全部已注册 Action 的私有字典由 lib/app/Sails.js 中Sails.prototype.getActions require(./get-actions)绑定为公开方法返回的是浅克隆而非引用通过_.clone(this._actions)生成一层拷贝。这样调用方无论对返回字典做什么操作增删键、覆盖值都不会污染 Sails 内部真实的 actions 字典。这一点在单元测试中有明确断言详见下文测试验证一节。需要说明的是浅克隆只复制了最外层字典value 中的 action 函数本身仍是原引用——这是符合预期的设计因为函数本身不应被克隆。Action 是从哪里来的加载与注册流程要理解getActions()返回的内容需要知道这些 Action 是如何进入sails._actions的。核心加载逻辑位于 lib/app/private/controller/load-action-modules.js1. 从磁盘加载Sails 启动时通过include-all扫描api/controllers目录默认路径可通过sails.config.paths.controllers配置并用两条正则区分文件类型传统控制器^((?:(?:.*)/)*([0-9A-Z][0-9a-zA-Z_]*))Controller\..$——PascalCase 且以Controller结尾文件内容是一个由多个 Action 组成的字典独立 Action 文件^((?:(?:.*)/)*([a-z][a-z0-9-]*))\..$——小写 kebab-case文件内容是一个函数或含fn的 machine 定义。对于传统控制器identity 由控制器路径 动作名拼接而成如somefolder.somecontroller.dostuff会规范化为somefolder/somecontroller/dostuff对于独立 Action 文件identity 就是去除扩展名后的文件路径如somefolder/dostuff。2. 冲突检测如果两个来源产生了相同的 identityhelpRegisterAction会抛出code E_CONFLICT的错误load-action-modules.js会捕获并补充文件路径等上下文信息后重新抛出。这正是getActions()返回的字典中键永远唯一的原因。3. 合并模块定义此外sails.config.controllers.moduleDefinitions中编程式定义的 Action 也会以force true的方式注册并覆盖磁盘加载的同名 Action参见 load-action-modules.js 末尾的合并逻辑。4. Hook 注册Sails 的 Hook 也可以通过自身的registerActions方法向sails._actions中注入 Action。比如 blueprints Hook 会注册影子路由所需的动作而 reloadActions() 会清空sails._actions后遍历所有 Hook 重新调用其registerActions再重新加载磁盘上的 Action 模块——这就是sails.getActions()的结果会同时包含用户代码与 Hook 注册的所有 Action 的原因。实际应用场景场景一运行时自省应用的请求处理能力在运行时打印当前应用的全部可用 Action是排查某个路由到底指向哪个 Action的快捷方式// 在 config/bootstrap.js 或任意 Hook 中 sails.after(lifted, function() { var actions sails.getActions(); console.log(当前应用共注册了 %d 个 Action:, Object.keys(actions).length); console.log(Object.keys(actions).sort().join(\n)); });场景二Hook 开发中按需查找 Action在 lib/app/private/controller/README.md 中getActions()被明确列为 Hook 开发者可以依赖的机制它提供了一种统一引用所有请求处理器的方式——既包含用户注册的 Action也包含 blueprints 等 Hook 注册的 Action。例如 blueprints Hook 在bindShadowRoutes中正是通过sails.getActions()获取快照再依据配置决定是否为相关模型生成影子路由见 lib/hooks/blueprints/index.js 第 188 行// 获取一份 actions 字典副本浅克隆 var actions sails.getActions();场景三与registerAction/registerActionMiddleware配合getActions()并不是孤立存在的它与同族的 Action API 形成完整闭环sails.registerAction(action, identity)编程式注册一个 Actionidentity 冲突时抛E_CONFLICT内部同样走helpRegisterActionsails.registerActionMiddleware(middleware, actionsGlobKey)为匹配某个 identity glob如user/*的 Action 注册前置中间件sails.reloadActions()清空并重新加载全部 Action。实践中你可以在注册了新的 Action例如通过sails.registerAction()之后调用sails.getActions()验证它是否已进入字典。测试验证行为有据可查仓库的单元测试 test/unit/controller.test.js 对getActions()的行为给出了权威印证第 278–301 行it(should return a shallow clone of the actions dictionary when sails.getActions is called, function() { var actions sailsApp.getActions(); assert(actions ! sailsApp._actions, sails.getActions is supposed to return a shallow clone, but got an exact reference!); // 期望的 identities 列表例如 // toplevellegacy/fnaction // toplevellegacy/underscore_action // toplevellegacy/action-with-dashes // top-level-standalone-fn // somefolder/someotherfolder/nestedlegacy/fnaction // ... var unexpectedActions _.difference(_.keys(actions), expectedActions); assert(!unexpectedActions.length, Loaded unexpected actions:...); _.each(expectedActions, function(expectedAction) { assert(actions[expectedAction], Did not load expected action expectedAction ); assert(_.isFunction(actions[expectedAction]), Expected action expectedAction loaded, but instead of a function it\s a typeof(actions[expectedAction])); }); });该测试同时验证了三件事返回的是新对象而非内部引用、identity 命名规则符合 kebab-case 约定、所有 Action 值均为函数——与官方文档的描述完全一致。使用注意事项只读语义getActions()返回浅克隆目的是防止调用方误改内部字典如果你确实要新增 Action请使用sails.registerAction()不要直接修改返回的字典。调用时机在应用完全 lift 之前例如某些早期 Hook 初始化阶段部分 Hook 可能尚未注册 Action此时返回的字典可能不完整若需要完整列表建议在sails.after(lifted, ...)之后调用。与 actions2 的关系虽然字典中的值都是(req, res)函数但 actions2 定义在注册时已被转换因此你无法从返回值中直接还原原始的 machine 定义如需 machine 元信息应在注册前自行保留。对 actions2 语法的背景可参考 ActionsAndControllers 与 GeneratingActions 文档。总结sails.getActions()是 Sails 应用运行时自省的入口之一它以扁平字典的形式暴露全部已注册 Action键为 kebab-case 身份标识、值为已统一为(req, res)形态的函数并通过浅克隆保护内部状态。理解它的实现get-actions.js、背后的加载流程load-action-modules.js与注册机制help-register-action.js能帮助你在 Hook 开发、路由调试和框架集成中更准确地把握应用的请求处理全貌。【免费下载链接】sailsRealtime MVC Framework for Node.js项目地址: https://gitcode.com/gh_mirrors/sa/sails创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/9/20 21:16:50

ZooKeeper客户端编程实战:从数据模型到分布式锁的避坑指南

分布式系统里,协调这件事听起来很虚,但落到代码上往往就是几个具体问题:多个进程怎么选出一个主节点、配置改了怎么让所有机器同时生效、某个节点挂了怎么让其他人立刻知道。ZooKeeper 就是为解决这类问题而生的。它对外暴露的接口非常朴素—…

2026/9/20 21:11:49

miniblink49 网页打印与 PDF 导出实战:从参数配置到故障排查

miniblink49 网页打印与 PDF 导出实战:从参数配置到故障排查 【免费下载链接】miniblink49 a lighter, faster browser kernel of blink to integrate HTML UI in your app. 一个小巧、轻量的浏览器内核,用来取代wke和libcef 项目地址: https://gitcod…

2026/9/20 22:06:52

R2R本地部署教程:一条命令跑起你的私有AI文档系统

R2R本地部署教程:一条命令跑起你的私有AI文档系统 【免费下载链接】R2R SoTA production-ready AI retrieval system. Agentic Retrieval-Augmented Generation (RAG) with a RESTful API. 项目地址: https://gitcode.com/GitHub_Trending/r2/R2R R2R 是一个…

2026/9/20 22:06:52

120ms 响应:如何让「本地AI数字人」跑进你的手机

120ms 响应&#xff1a;如何让「本地AI数字人」跑进你的手机 【免费下载链接】Duix-Mobile &#x1f680; The best real-time interactive AI avatar(digital human) with on-premise deployment and <1.5 s latency. 项目地址: https://gitcode.com/GitHub_Trending/du/…

2026/9/20 0:04:49

GAMP 5 基于风险的计算机化系统验证:软件分类与审计追踪实践

简介&#xff1a;《A Risk-Based Approach to Compliant GxP Computerized Systems》即业内熟知的GAMP 5指南&#xff0c;面向制药企业质量与IT合规人员、验证工程师及计算机化系统管理者&#xff0c;用于解决GxP法规环境下系统合规性难以科学落地的问题。文档以风险管理为主线…

2026/9/20 0:04:49

安全托管MSSP实战:从静态防御到人机协同的攻防运营与应急响应

简介&#xff1a;这份PPT围绕互联网业务安全托管服务展开&#xff0c;面向企业安全负责人、IT运维人员及关注MSSP/MSS选型的读者&#xff0c;重点回应传统安全过度依赖人工、碎片化静态防御难以对抗产业化攻击等痛点。资源共1个pptx文件&#xff0c;包体约30.63MB&#xff0c;以…

2026/9/20 0:04:49

GAMP 5 基于风险的计算机化系统验证:软件分类与审计追踪实践

简介&#xff1a;《A Risk-Based Approach to Compliant GxP Computerized Systems》即业内熟知的GAMP 5指南&#xff0c;面向制药企业质量与IT合规人员、验证工程师及计算机化系统管理者&#xff0c;用于解决GxP法规环境下系统合规性难以科学落地的问题。文档以风险管理为主线…

2026/9/20 0:04:49

安全托管MSSP实战:从静态防御到人机协同的攻防运营与应急响应

简介&#xff1a;这份PPT围绕互联网业务安全托管服务展开&#xff0c;面向企业安全负责人、IT运维人员及关注MSSP/MSS选型的读者&#xff0c;重点回应传统安全过度依赖人工、碎片化静态防御难以对抗产业化攻击等痛点。资源共1个pptx文件&#xff0c;包体约30.63MB&#xff0c;以…

2026/9/20 4:54:47

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行&#xff0c;Type-C接口算是典型的“看着简单&#xff0c;做起来全坑”的东西。光引脚就24个&#xff0c;高低速信号、电源、控制线全部塞在一个小小的连接器里&#xff0c;如果PCB布局不做规划&#xff0c;打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/20 5:01:23

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/20 5:09:33

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码