如何使用Golang结合V8Go调用JavaScript匿名函数?
Golang结合V8Go调用JSON-Mask JS文件返回null的修正方案
问题背景
现有一个实现JSON字段掩码功能的JavaScript文件,Java代码可正常调用其中的匿名函数完成掩码处理,但改用Golang + V8Go库实现时,当前代码调用后返回null且无法定位问题,需修正实现逻辑。
Java参考实现
public class JavaScriptEngine { private ScriptObjectMirror mirror; private Object eval; public JavaScriptEngine() throws IOException, ScriptException { ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("JavaScript"); /** * Use the line below to load the file directly from a CDN, this is recommended to get new versions automatically * Object eval = engine.eval("load('https://cdn.com/json-mask.js')"); */ String absolutePath = new File("src/main/resources/js-code/json-mask.js").getAbsolutePath(); Object eval = engine.eval(Files.newBufferedReader(Paths.get(absolutePath), StandardCharsets.UTF_8)); this.mirror = ((ScriptObjectMirror) ((ScriptObjectMirror) eval).get("default")); this.eval = eval; } public String call(String field, String type) { String json = "{\"value\": \"" + field + "\" }"; String setting = "[{\"type\": \""+ type.toUpperCase()+ "\", \"path\": \"$.value\"}]"; Object aDefault = mirror.call(eval, json, setting); Object read = JsonPath.parse(aDefault.toString()).read("$.value"); return read == null ? null : read.toString(); } }
当前存在问题的Go实现
package main import ( "encoding/json" "fmt" "os" v8 "rogchap.com/v8go" ) func main() { fmt.Println("test") content, err := os.ReadFile("json-mask.js") fmt.Println(err) ctx := v8.NewContext() result, errorJS := ctx.RunScript(string(content), "file.js") fmt.Println(result) fmt.Println(errorJS) // the val returns null, I think I'm calling it wrong val, _ := ctx.RunScript("result", "value.js") fmt.Printf("addition result: %s", val) }
修正后的Go实现方案
问题分析
- 原代码未将JS脚本的导出对象挂载到全局上下文,直接引用
result变量会返回null - 未正确提取JS模块中的
default函数(对应Java中的mirror对象) - 缺少函数调用的参数构造与执行逻辑
- 错误处理不完善,无法定位中间环节的问题
修正代码
package main import ( "encoding/json" "fmt" "os" v8 "rogchap.com/v8go" ) func main() { fmt.Println("test") // 读取JS文件 content, err := os.ReadFile("json-mask.js") if err != nil { fmt.Printf("读取JS文件失败: %v\n", err) return } // 创建V8上下文 ctx := v8.NewContext() // 运行JS脚本,获取模块导出对象 exportObj, err := ctx.RunScript(string(content), "json-mask.js") if err != nil { fmt.Printf("运行JS脚本失败: %v\n", err) return } fmt.Println("JS脚本导出对象:", exportObj) // 从导出对象中获取default函数 defaultFuncVal, err := exportObj.AsObject().Get("default") if err != nil { fmt.Printf("获取default函数失败: %v\n", err) return } if !defaultFuncVal.IsFunction() { fmt.Println("导出的default不是函数类型") return } defaultFunc := defaultFuncVal.AsFunction() // 构造测试参数(模拟Java call方法的输入) testField := "敏感信息123456" testType := "MASK" jsonParam := fmt.Sprintf(`{"value": "%s"}`, testField) settingParam := fmt.Sprintf(`[{"type": "%s", "path": "$.value"}]`, testType) // 将参数转换为V8可识别的值 jsonV8Val, err := v8.NewValue(ctx, jsonParam) if err != nil { fmt.Printf("构造JSON参数失败: %v\n", err) return } settingV8Val, err := v8.NewValue(ctx, settingParam) if err != nil { fmt.Printf("构造配置参数失败: %v\n", err) return } // 调用default函数,第一个参数为this对象(对应Java中的eval) resultV8Val, err := defaultFunc.Call(exportObj, jsonV8Val, settingV8Val) if err != nil { fmt.Printf("调用掩码函数失败: %v\n", err) return } // 解析返回结果,提取value字段 resultStr := resultV8Val.String() var resultMap map[string]interface{} if err := json.Unmarshal([]byte(resultStr), &resultMap); err != nil { fmt.Printf("解析结果JSON失败: %v\n", err) return } maskedValue := resultMap["value"] fmt.Printf("原始值: %s\n处理后的值: %v\n", testField, maskedValue) }
关键修正点
- 完善错误处理:每一步操作都检查错误,便于快速定位问题
- 正确提取导出函数:从JS脚本运行结果中获取
default函数,确保调用的是正确的处理逻辑 - 参数转换与传递:将Go字符串参数转为V8可识别的
Value类型,并正确传入函数 - 模拟Java的调用逻辑:函数调用时传入
this对象(对应Java中的eval),保证JS函数内部上下文正确 - 结果解析:将V8返回的结果转为字符串后,用Go标准库解析JSON并提取目标字段
内容的提问来源于stack exchange,提问作者Marcelo Maico
相关产品推荐
相关产品推荐

