如何实现支持Ruby变量与对象插值的用户自定义Webhook JSON动态模板
问题核心根源
当前用String#%输出不符合预期,本质是执行流程的顺序错位:
String#%是纯文本层面的替换逻辑,无论占位符对应的原始值是什么类型,都会强制调用to_s转成普通字符串- 现有流程是先在纯字符串层面完成所有占位符替换,再把替换完的字符串转成JSON。此时所有嵌套对象早已经被转成Ruby语法格式的字符串,自然没法输出结构化的嵌套JSON节点。
可行实现方案
1. 结构化模板替换(最稳定、无安全风险)
完全绕开“先把全模板当字符串替换、再转JSON”的思路,把用户提交的模板当成和最终输出同结构的JSON/YAML数据,做递归遍历替换:
- 规则设计:沿用大家熟悉的
%{key}或者{{key}}占位符格式,直接把占位符作为模板节点的值,不强制把占位符包裹成纯字符串值 - 遍历逻辑:
- 碰到普通节点值时,先判断这个值是不是“纯占位符”(也就是整个字符串就是占位符标记,没有其他额外文本)
- 如果是纯占位符,直接把变量上下文里对应key的原始结构化对象塞到这个节点的位置,不做任何字符串转换
- 如果占位符只是字符串的一部分(比如
"用户%{username} 的反馈已收到"),就对这个字符串做常规的文本替换,生成普通字符串值 - 如果碰到嵌套的哈希、数组结构,就递归执行上面的判断逻辑
举个实际的模板示例:
{ "id": "%{id}", "message": "%{message}", "badge": "%{badge}" }
替换时,因为%{badge}是纯占位符,会直接把badge对应的嵌套结构化对象放到对应位置,最终输出的JSON完全符合预期,不会出现字符串化的哈希内容。
这个方案的优势:
- 无任何代码执行风险,整个流程只是固定规则的占位符匹配,完全不涉及用户提交内容的代码执行
- 对嵌套结构的控制完全可控,什么时候输出结构化内容、什么时候输出文本,规则清晰没有歧义
- 实现成本极低,递归遍历的逻辑几十行代码就能写完,不需要引入重型模板引擎
2. 兼容现有%{}语法的调整方案
如果不想换掉用户已经习惯的%{key}占位符格式,只需要调整执行顺序即可解决问题:
- 不再先对整个模板字符串做
String#%替换 - 先把用户写的模板解析成Ruby原生的Hash/数组结构,再递归遍历每个节点做替换
- 递归过程中碰到纯占位符的字符串值,直接取出对应原始值替换;碰到嵌在文本里的占位符,再调用
String#%做文本拼接 - 等整个结构全部替换完成后,最后统一调用
to_json输出最终载荷
这个调整只要把原来“先替换、再转结构”的流程反过来,就能彻底解决嵌套对象被字符串化的问题。
不推荐的踩坑选项
- ERB:虽然能实现动态替换,但允许执行任意Ruby代码,哪怕加沙箱限制也很容易被绕过,绝对不适合开放给外部用户自定义模板使用
- RABL、Jsonnet这类方案:要么是面向服务端开发的固定模板,没法灵活支持用户自定义的动态结构;要么语法学习成本太高,普通用户很难快速上手写出符合要求的内容
内容的提问来源于stack exchange,提问作者Peter P.
相关产品推荐
相关产品推荐

