如何将数据添加到在 Go 中作为参数的接口?
How do I add data to an interface that is taken as an argument in Go?
我刚刚开始,如果我的术语不准确,请见谅。我的最终目标是将缓存无效的 CSS 文件的名称添加到我的 Go 应用程序的布局模板中。 CSS 文件是在应用程序启动时即时构建的,因此无法进行硬编码。在我的模板文件中我有这个:
//More html here
<link href="{{.CSSFile}}" rel="stylesheet">
//more html here
我在视图类型上有一个渲染方法,如下所示。它以 data interface{}
作为参数,然后运行 ExecuteTemplate
。每个控制器以一种或另一种方式调用它,发送 data
参数并公开信息。我知道如何将它作为数据从控制器添加,然后调用 Render
方法,但我显然不想在每个控制器操作中添加 CSS 文件,所以它最有意义一次将其添加到 Render 函数中,并将其添加到传递给 ExecuteTemplate
的数据中。我的问题是如何将此信息添加到已传递给 Render
的数据中,然后将整个信息传递给 ExecuteTemplate
。我下面的内容适用于 CSS 文件,但它显然不会发送传递给原始 Render
参数的 data
。
type View struct {
Template *template.Template
Layout string
}
func (v *View) Render(w http.ResponseWriter, data interface{}) error {
d := Data{}
d.AddCSSFile()
w.Header().Set("Content-Type", "text/html")
err := v.Template.ExecuteTemplate(w, v.Layout, d)
if err != nil {
log.Println(err)
fmt.Fprintln(w, "<h1>Something went wrong. Please contact us at support")
}
return nil
}
type Data struct {
Alerts []Alert
Yield interface{}
CSSFile interface{}
}
func (d *Data) AddCSSFile() {
ss, _ := filepath.Glob("./assets/site-*.css")
fp := strings.Join(ss, "")
_, d.CSSFile = filepath.Split(fp)
}
我创建了一个要点,它虽然不完全完整,但比我正在尝试做的事情更加充实:
https://gist.github.com/codelitt/549a68149add0482c6dc2514a46aa580
不确定我是否完全理解您的要求,但如果您想要将 data interface{}
参数与 Render
中的 d := Data{}
值结合起来,那么您可以做一些事情像这样...
// ...
func (v *View) Render(w http.ResponseWriter, data interface{}) error {
p := Page{Data:data}
p.AddCSSFile()
w.Header().Set("Content-Type", "text/html")
err := v.Template.ExecuteTemplate(w, v.Layout, p)
if err != nil {
log.Println(err)
fmt.Fprintln(w, "<h1>Something went wrong. Please contact us at support")
}
return nil
}
type Page struct {
Alerts []Alert
Yield interface{}
CSSFile interface{}
Data interface{}
}
func (p *Page) AddCSSFile() {
// ...
}
编辑: 或者您也可以只初始化一个匿名结构值并将其传递给 ExecuteTemplate
,而无需更改现有的 Data
类型。
// ...
err := v.Template.ExecuteTemplate(w, v.Layout, struct{
Data
Args interface{}
}{Data:d, Args:data})
// ...
Edit2: 因此,如果我正确理解您的评论,则传递给 Render
方法的 data interface{}
参数在某些或所有情况下可能是或包含一个匹配 Data
字段类型之一的类型的值,在这种情况下,您希望将该值设置为该字段,以便将其一起传递给 ExecuteTemplate
方法。正如您已经发现的那样,至少一种解决方案是使用类型断言。以下是您的示例的略微修改版本,来自问题中原始示例上下文中的评论。
func (v *View) Render(w http.ResponseWriter, data interface{}) error {
d := Data{}
d.AddCSSFile()
if alerts, ok := data.([]Alert); ok {
d.Alerts = alerts
}
w.Header().Set("Content-Type", "text/html")
err := v.Template.ExecuteTemplate(w, v.Layout, d)
if err != nil {
log.Println(err)
fmt.Fprintln(w, "<h1>Something went wrong. Please contact us at support")
}
return nil
}
我刚刚开始,如果我的术语不准确,请见谅。我的最终目标是将缓存无效的 CSS 文件的名称添加到我的 Go 应用程序的布局模板中。 CSS 文件是在应用程序启动时即时构建的,因此无法进行硬编码。在我的模板文件中我有这个:
//More html here
<link href="{{.CSSFile}}" rel="stylesheet">
//more html here
我在视图类型上有一个渲染方法,如下所示。它以 data interface{}
作为参数,然后运行 ExecuteTemplate
。每个控制器以一种或另一种方式调用它,发送 data
参数并公开信息。我知道如何将它作为数据从控制器添加,然后调用 Render
方法,但我显然不想在每个控制器操作中添加 CSS 文件,所以它最有意义一次将其添加到 Render 函数中,并将其添加到传递给 ExecuteTemplate
的数据中。我的问题是如何将此信息添加到已传递给 Render
的数据中,然后将整个信息传递给 ExecuteTemplate
。我下面的内容适用于 CSS 文件,但它显然不会发送传递给原始 Render
参数的 data
。
type View struct {
Template *template.Template
Layout string
}
func (v *View) Render(w http.ResponseWriter, data interface{}) error {
d := Data{}
d.AddCSSFile()
w.Header().Set("Content-Type", "text/html")
err := v.Template.ExecuteTemplate(w, v.Layout, d)
if err != nil {
log.Println(err)
fmt.Fprintln(w, "<h1>Something went wrong. Please contact us at support")
}
return nil
}
type Data struct {
Alerts []Alert
Yield interface{}
CSSFile interface{}
}
func (d *Data) AddCSSFile() {
ss, _ := filepath.Glob("./assets/site-*.css")
fp := strings.Join(ss, "")
_, d.CSSFile = filepath.Split(fp)
}
我创建了一个要点,它虽然不完全完整,但比我正在尝试做的事情更加充实: https://gist.github.com/codelitt/549a68149add0482c6dc2514a46aa580
不确定我是否完全理解您的要求,但如果您想要将 data interface{}
参数与 Render
中的 d := Data{}
值结合起来,那么您可以做一些事情像这样...
// ...
func (v *View) Render(w http.ResponseWriter, data interface{}) error {
p := Page{Data:data}
p.AddCSSFile()
w.Header().Set("Content-Type", "text/html")
err := v.Template.ExecuteTemplate(w, v.Layout, p)
if err != nil {
log.Println(err)
fmt.Fprintln(w, "<h1>Something went wrong. Please contact us at support")
}
return nil
}
type Page struct {
Alerts []Alert
Yield interface{}
CSSFile interface{}
Data interface{}
}
func (p *Page) AddCSSFile() {
// ...
}
编辑: 或者您也可以只初始化一个匿名结构值并将其传递给 ExecuteTemplate
,而无需更改现有的 Data
类型。
// ...
err := v.Template.ExecuteTemplate(w, v.Layout, struct{
Data
Args interface{}
}{Data:d, Args:data})
// ...
Edit2: 因此,如果我正确理解您的评论,则传递给 Render
方法的 data interface{}
参数在某些或所有情况下可能是或包含一个匹配 Data
字段类型之一的类型的值,在这种情况下,您希望将该值设置为该字段,以便将其一起传递给 ExecuteTemplate
方法。正如您已经发现的那样,至少一种解决方案是使用类型断言。以下是您的示例的略微修改版本,来自问题中原始示例上下文中的评论。
func (v *View) Render(w http.ResponseWriter, data interface{}) error {
d := Data{}
d.AddCSSFile()
if alerts, ok := data.([]Alert); ok {
d.Alerts = alerts
}
w.Header().Set("Content-Type", "text/html")
err := v.Template.ExecuteTemplate(w, v.Layout, d)
if err != nil {
log.Println(err)
fmt.Fprintln(w, "<h1>Something went wrong. Please contact us at support")
}
return nil
}