在Go模板中请求上下文

I would like to write such a conditional fragment in a Go HTML template :

  {{if isUserAdmin}}
     <a href"/admin/nuke">Go to the big red nuclear button</a>
  {{end}}

However, this is not directly possible because the template is not aware of the request that triggered its execution, so it cannot determine if the user is admin or not.

Is there some normal way to achieve this ?

In advance I point out that :

  • I do not want to use Pipelines for this specific data (see other question about this)
  • I acknowledge that only the handlers/controllers should deal with logic, and views should only do the rendering. But the condition {{if isUserAdmin}} is not logic itself, it's a necessary construct to leverage a boolean value already calculated by the controller.
  • The Funcs method can help, but is currently not lean enough for easily defining specific method isUserAdmin()

Here is a working solution attempt (link to Playground) using Funcs to overwrite "isAdmin", after template compilation but before each execution (thanks to Valentin CLEMENT in other question).

But it has several flaws :

  • It is weird to declare a dummy empty "isAdmin" function before template compilation.
  • (Using Funcs several times is painful because I cannot just overwrite a single method, I have to provide a complete FuncMap with all the functions) edit : in fact previous funcs are not lost, i was wrong about that.
  • It is inherently not thread-safe and will fail when several goroutines alter and execute the same template

The normal thing to do is to simply pass your template a struct with whatever static data you like. Unless I've misunderstood what you're trying to do, there doesn't seem to be any need for Funcs here. Simplifying your example:

package main

import (
    "html/template"
    "os"
)

const hometmpl = `
{{if .IsAdmin}}
  <a href="/admin/nuke">Go to the big red nuclear button</a>
{{end}}
`

var t = template.Must(template.New("home").Parse(hometmpl))

func isAdmin(token string) bool {
    const adminToken = "0xCAFEBABE"
    return token == adminToken
}

func main() {
    token := "0xCAFEBABE" // Or extracted from the http.Request
    t.ExecuteTemplate(os.Stdout, "home", struct{IsAdmin bool}{isAdmin(token)})
}

I would agree with Darshan Computing, I think the proper way of passing information from the request would be in the pipeline. You can have your data being split between the data you want to render and the context, e.g. by having your template data structure embed them both if you want to clearly separate the two:

type TemplateData struct {
    *Content
    *Context
}

Which gives this for example. You can then reuse some of your context/content information depending on what is shared and what is query specific.