How do I add data to an interface that is taken as an argument in Go?

huangapple go评论82阅读模式
英文:

How do I add data to an interface that is taken as an argument in Go?

问题

我刚开始学习Go语言,如果我的术语不准确,请谅解。我的最终目标是将缓存破坏的CSS文件名添加到Go应用程序的布局模板中。CSS文件在应用程序启动时动态生成,因此不能硬编码。在我的模板文件中,我有以下内容:

//更多HTML代码
<link href="{{.CSSFile}}" rel="stylesheet">
//更多HTML代码

我在View类型上有一个Render方法,如下所示。它以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)
}

我创建了一个Gist,虽然不完整,但更详细地说明了我想要做的事情:
https://gist.github.com/codelitt/549a68149add0482c6dc2514a46aa580

英文:

I'm just picking up go, so apologies if my terminology isn't precise. My end goal is to add the name of a cachebusted CSS file to the layout template of my Go application. The CSS file built on the fly when the application starts up so can't be hardcoded. In my template file I have this:

//More html here
&lt;link href=&quot;{{.CSSFile}}&quot; rel=&quot;stylesheet&quot;&gt;
//more html here 

I have a Render method on a View type like shown below. It takes data interface{} as an argument and then runs ExecuteTemplate. It is called by every controller in one way or another that sends the data argument and exposes information. I know how to add it as data from the controller that then calls the Render method, but I obviously don't want to add the CSS file in every single controller action, so it makes the most sense to add it in the Render function one time and have it added to the data that gets passed to ExecuteTemplate. My issue is how do I add this information to the data already being passed to Render and then pass that whole of information to ExecuteTemplate. What I have below works for the CSS file, but it obviously doesn't send along the data that was passed to the original Render argument.

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(&quot;Content-Type&quot;, &quot;text/html&quot;)
	err := v.Template.ExecuteTemplate(w, v.Layout, d)
	if err != nil {
		log.Println(err)
		fmt.Fprintln(w, &quot;&lt;h1&gt;Something went wrong. Please contact us at support&quot;)
	}
	return nil
}

type Data struct {
	Alerts  []Alert
	Yield   interface{}
	CSSFile interface{}
}


func (d *Data) AddCSSFile() {
	ss, _ := filepath.Glob(&quot;./assets/site-*.css&quot;)
	fp := strings.Join(ss, &quot;&quot;)
	_, d.CSSFile = filepath.Split(fp)
}

I've created a gist which, not entirely complete, is a little more fleshed out of what I'm trying to do:
https://gist.github.com/codelitt/549a68149add0482c6dc2514a46aa580

答案1

得分: 2

我不确定我完全理解你的问题,但如果你想要将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() {
     // ...
}

// ...

err := v.Template.ExecuteTemplate(w, v.Layout, struct{
    Data
    Args interface{}
}{Data:d, Args:data})

// ...

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
}

希望能对你有所帮助!

英文:

Not sure I understand exactly what you're asking but if what you want is to combine the data interface{} argument with the d := Data{} value inside Render, then you could do something like this...

// ...
func (v *View) Render(w http.ResponseWriter, data interface{}) error {
p := Page{Data:data}
p.AddCSSFile()
w.Header().Set(&quot;Content-Type&quot;, &quot;text/html&quot;)
err := v.Template.ExecuteTemplate(w, v.Layout, p)
if err != nil {
log.Println(err)
fmt.Fprintln(w, &quot;&lt;h1&gt;Something went wrong. Please contact us at support&quot;)
}
return nil
}
type Page struct {
Alerts  []Alert
Yield   interface{}
CSSFile interface{}
Data interface{}
}
func (p *Page) AddCSSFile() {
// ...
}

Edit: Or you could also just initialize an anonymous struct value and pass it to ExecuteTemplate without having to change you existing Data type.

// ...
err := v.Template.ExecuteTemplate(w, v.Layout, struct{
Data
Args interface{}
}{Data:d, Args:data})
// ...

Edit2: So if I understand your comment correctly the data interface{} argument passed to the Render method could in some or all instances be or contain a value of a type that matches one of the Data fields' types, in which case you would like to set that value to that field so as to pass it together to the ExecuteTemplate method. At least one solution to that, as you've already found out, is to use type assertion. Below is a slightly modified version of your example from the comment in the context of your original example from the question.

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(&quot;Content-Type&quot;, &quot;text/html&quot;)
err := v.Template.ExecuteTemplate(w, v.Layout, d)
if err != nil {
log.Println(err)
fmt.Fprintln(w, &quot;&lt;h1&gt;Something went wrong. Please contact us at support&quot;)
}
return nil
}

huangapple
  • 本文由 发表于 2017年3月16日 22:04:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/42836313.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定