You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go中按用户类型条件渲染模板:解决模板未定义错误

问题描述

我有一个网页包含匿名用户和管理员两种视图,仅需为管理员显示导航栏,其余内容对两类用户保持一致。以下是我尝试实现的代码:

main.go

package main

import (
    "log"
    "net/http"
    "text/template"

    "github.com/julienschmidt/httprouter"
)

func BasicAuth(h httprouter.Handle, requiredUser, requiredPassword string) httprouter.Handle {
    return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
        // Get the Basic Authentication credentials
        user, password, hasAuth := r.BasicAuth()

        if hasAuth && user == requiredUser && password == requiredPassword {
            // Delegate request to the given handle
            h(w, r, ps)
        } else {
            // Request Basic Authentication otherwise
            w.Header().Set("WWW-Authenticate", "Basic realm=Restricted")
            http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
        }
    }
}

func Anonymous(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
    t, err := template.ParseFiles("index.html")
    if err != nil {
        log.Fatalln(err)
    }
    err = t.Execute(w, map[string]string{"Name": "Anonymous"})
    if err != nil {
        log.Fatalln(err)
    }
}

func Admin(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
    t, err := template.ParseFiles("index.html", "admin.html")
    if err != nil {
        log.Fatalln(err)
    }
    err = t.Execute(w, map[string]string{"Name": "Admin"})
    if err != nil {
        log.Fatalln(err)
    }
}

func main() {
    user := "admin"
    pass := "1234"

    router := httprouter.New()
    router.GET("/", Anonymous)
    router.GET("/admin/", BasicAuth(Admin, user, pass))

    log.Fatal(http.ListenAndServe(":8080", router))
}

index.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>{{ .Name }}</title>
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" rel="stylesheet">
        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.min.js"></script>
        <script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
        <script>
            function counter() {
                document.getElementById("x").innerHTML = "x: " + document.querySelectorAll('.x').length;
                document.getElementById("y").innerHTML = "y: " + document.querySelectorAll('.y').length;
                document.getElementById("z").innerHTML = "z: " + document.querySelectorAll('.z').length;
            }
        </script>
    </head>
    <body onload="counter()">
        {{ template "dashboard" }}
        <nav class="navbar fixed-bottom">
            <div class="container-fluid nav-justified">
                <span id="x" class="navbar-brand nav-item"></span>
                <span id="y" class="navbar-brand nav-item"></span>
                <span id="z" class="navbar-brand nav-item"></span>
            </div>
        </nav>
    </body>
</html>

admin.html

{{ define "dashboard" }}
<nav class="navbar">
    <div class="container-fluid nav-justified">
        <span class="nav-item">
            <a class="navbar-brand" href="/a">a</a>
        </span>
        <span class="nav-item">
            <a class="navbar-brand" href="/b">b</a>
        </span>
        <span class="nav-item">
            <a class="navbar-brand" href="/c">c</a>
        </span>
    </div>
</nav>
{{ end }}

运行后遇到错误:

template: index.html:18:14: executing "index.html" at <{{template "dashboard"}}>: template "dashboard" not defined

我原本以为匿名用户渲染时未传入admin.html模板,dashboard模板不会被解析,但实际报错了,请问如何修复该问题,或是否有更优的实现方式?


解决方案

方法一:为匿名用户补充空模板定义

Go模板执行时会严格校验所有引用的模板是否存在,哪怕你没打算渲染它。可以在index.html中默认定义一个空的dashboard模板,管理员加载admin.html时会自动覆盖这个空定义:

修改index.html,在文件末尾添加空模板:

<!DOCTYPE html>
<html lang="en">
    <!-- 原有头部、body内容不变 -->
</html>

{{ define "dashboard" }}
<!-- 空内容,匿名用户无导航 -->
{{ end }}

这样匿名用户解析index.html时就有了dashboard的定义,管理员加载admin.html后会用管理员导航模板替换空内容,满足需求。

方法二:用数据条件控制导航显示

放弃模板定义的方式,直接通过传入模板的字段判断是否渲染管理员导航:

修改index.html的body部分:

<body onload="counter()">
    {{ if eq .Role "admin" }}
    <nav class="navbar">
        <div class="container-fluid nav-justified">
            <span class="nav-item">
                <a class="navbar-brand" href="/a">a</a>
            </span>
            <span class="nav-item">
                <a class="navbar-brand" href="/b">b</a>
            </span>
            <span class="nav-item">
                <a class="navbar-brand" href="/c">c</a>
            </span>
        </div>
    </nav>
    {{ end }}
    <!-- 底部导航不变 -->
</body>

修改main.go的处理器,统一解析index.html并传入角色字段:

func Anonymous(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
    t, err := template.ParseFiles("index.html")
    if err != nil {
        log.Fatalln(err)
    }
    err = t.Execute(w, map[string]string{"Name": "Anonymous", "Role": "guest"})
    if err != nil {
        log.Fatalln(err)
    }
}

func Admin(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
    t, err := template.ParseFiles("index.html")
    if err != nil {
        log.Fatalln(err)
    }
    err = t.Execute(w, map[string]string{"Name": "Admin", "Role": "admin"})
    if err != nil {
        log.Fatalln(err)
    }
}

这种方式逻辑更直观,不需要额外的模板文件,适合简单场景。

方法三:预加载模板并通过参数控制渲染

生产环境推荐预加载所有模板,减少运行时解析开销,同时通过参数控制是否显示管理员导航:

修改main.go,预加载模板:

var tmpl *template.Template

func init() {
    var err error
    tmpl, err = template.ParseFiles("index.html", "admin.html")
    if err != nil {
        log.Fatalln(err)
    }
}

func Anonymous(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
    err := tmpl.ExecuteTemplate(w, "index.html", map[string]bool{"IsAdmin": false})
    if err != nil {
        log.Fatalln(err)
    }
}

func Admin(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
    err := tmpl.ExecuteTemplate(w, "index.html", map[string]bool{"IsAdmin": true})
    if err != nil {
        log.Fatalln(err)
    }
}

修改index.html的模板引用逻辑:

<body onload="counter()">
    {{ if .IsAdmin }}
        {{ template "dashboard" }}
    {{ end }}
    <!-- 底部导航不变 -->
</body>

这种方式只在程序启动时解析一次模板,性能更优,适合高并发场景。


内容的提问来源于stack exchange,提问作者user21795212

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 06:53:09