【遇到一个怪异的问题】使用embed来加载模版,只要写在init()函数中就会导致HTTP服务出错

作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢!


经过反复测试,只要换一个写法就能开启/重现这个怪异的问题。
请看出现异常的代码:

//go:embed index.html
var indexFileTemplate string
var indexTempate = template.New("index")

//go:embed mysql_data_source.html
var mysqlDataSourceFileTemplate string

var mysqlDataSourceTempate = template.New("mysql_data_source")

// var mysqlDataSourceTempate = template.Must(template.New("mysql_data_source").Parse(mysqlDataSourceFileTemplate))

func init() {
	indexTempate = template.Must(indexTempate.Parse(indexFileTemplate))
	mysqlDataSourceTempate = template.Must(indexTempate.Parse(mysqlDataSourceFileTemplate))
}

使用上述的代码,gin框架中访问首页,在执行模版的时候报这样的错误:

template: index:9:49: executing "index" at <.Name>: can't evaluate field Name in type *page.templateParam

我在index.html中没有{{.Name}},只在mysql_data_source.html中有使用。不管怎么样,我访问首页,和另一个页面没关系鸭!

换个写法就能正常打开首页:

//go:embed index.html
var indexFileTemplate string
var indexTempate = template.New("index")

//go:embed mysql_data_source.html
var mysqlDataSourceFileTemplate string

var mysqlDataSourceTempate = template.New("mysql_data_source")

var mysqlDataSourceTempate = template.Must(template.New("mysql_data_source").Parse(mysqlDataSourceFileTemplate))

func init() {
	indexTempate = template.Must(indexTempate.Parse(indexFileTemplate))
	//mysqlDataSourceTempate = template.Must(indexTempate.Parse(mysqlDataSourceFileTemplate))
}

简直是无语了……


猜测可能的原因:
embed对应的字符串的加载,是比init()函数还要晚的。当init函数执行的时候,embed的字符串还没加载,这样就导致了模版解析出错。然而,template.Must()并不会导致panic,只是默默地记录下了这个错误。等到模版真正被使用的时候,抛出模版加载期导致的错误,进而出现上述的现象。解决办法就是不要在init函数中写模版加载的代码。

已经把这个问题提到了官方:https://github.com/golang/go/issues/56952


11-29补充:
已经得到了官方的回答,原因还是赋值语句和init()函数的先后顺序导致的。
我自己的代码,假定赋值先执行,然后init()函数再执行。
但是,如果init()先执行,而赋值后执行,就会导致indexTempate对象处于未初始化的状态。
因此,解决办法是不要假定初始化执行的顺序,统一只在一个地方初始化。

posted on 2022-11-28 11:20  ahfuzhang  阅读(130)  评论(0)    收藏  举报