如何将数据添加到在 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
}