使用Go模板解析以纯文本形式显示的页面中的HTML代码

5
我开始使用Go语言,现在正试图创建一个HTML页面。我对Go编程和HTML都很陌生。
在我的Go程序中,我正在尝试将HTML页面(div)解析为主HTML页面(index.html),并尝试使用Go中的模板将此div添加到主HTML页面中。
我正在尝试解析数据的HTML页面如下:
<html>
<head>
    <meta charset="utf-8">
    <title>SmartHomePi Status</title>
    <link rel="stylesheet" href="../static/css/style.css">
</head>
<body>
    <h1>HomeControl Connection Status</h1>
    <div id="wrapper" width="100%">
        {{.SonoffBasic}}
    </div>
</body>

{{.SonoffBasic}} 部分是我尝试添加 div 的部分。 我想要添加的 div 如下所示:

<div id="sonoffbasicdevice">
    <span class="%connectiondotclass%"></span>
    <p id="title">%title%</p>
    <p id="tempandhum">%humstatus%</p>
    <p id="ipaddress">%ipaddress%</p>
    <p id="portnumber">%portnumber%</p>
</div>

所有带有%xxx%的值在Go中被解析为包含有用信息的内容,并且一切都正确。
但是现在我正在尝试在Go中使用模板将这两个值解析在一起,但似乎出了些问题。
我正在使用以下Go函数来实现:
func renderHomeKitTemplate(w http.ResponseWriter, tmpl string, items *WebPageHTMLItems) {
    err := templates.ExecuteTemplate(w, "index.html", items)
} 

变量items是一个结构体,长这样:

type WebPageHTMLItems struct{
    SonoffBasic  string
}

其中theSonoffBasic字符串包含所有填写信息的div。

当我运行程序时,可以查看网页,但是当网页显示时,它显示为纯文本,我在这里做错了什么? HTML代码显示为文本

这是否是将数据解析到HTML文件中的正确方法。我使用这种添加完整div的方式的原因是我可以拥有多个“sonoffbasicdevice”项目并应该添加多个。


可能是Go template.ExecuteTemplate include html的重复问题。 - Jonathan Hall
2个回答

6

使用 template.HTML

html/template 提供了自动的上下文敏感转义,防止代码注入:

HTML 模板将数据值视为纯文本,应该进行编码,以便可以安全地嵌入到 HTML 文档中。转义是上下文相关的,因此操作可以出现在 JavaScript、CSS 和 URI 上下文中。

html/template 包中有一种特殊类型:template.HTML。模板中的这种类型的值在渲染模板时不会被转义。

因此,将 WebPageHTMLItems.SonoffBasic 的类型更改为 template.HTML,它将按原样呈现:

type WebPageHTMLItems struct{
    SonoffBasic template.HTML
}

template.HTML的底层类型是string,因此要从string值设置此字段,只需使用简单的类型转换:

params := &WebPageHTMLItems{
    SonoffBasic: template.HTML("<div>...</div>"),
}

警告:这不会防止代码注入!例如,当您替换%title%并且其中包含JavaScript代码时,该代码将被放到输出中,并最终在客户端浏览器中执行。
使用{{template}}操作
还要注意,您可以将<div>定义为单独的命名模板,并只使用{{template}}操作将其包含在页面中,类似于以下内容:
{{define "sonoffbasicdevice"}}
    <div id="sonoffbasicdevice">
        <span class="%connectiondotclass%"></span>
        <p id="title">%title%</p>
        <p id="tempandhum">%humstatus%</p>
        <p id="ipaddress">%ipaddress%</p>
        <p id="portnumber">%portnumber%</p>
    </div>
{{end}}

在你的页面模板中:

<div id="wrapper" width="100%">
    {{template "sonoffbasicdevice"}}
</div>

此外,请注意要完全安全,您也可以像对待其他模板一样制作并传递参数给您的 sonoffbasicdevice 模板,并让 html/template 包负责进行安全的、上下文相关的替换。
{{define "sonoffbasicdevice"}}
    <div id="sonoffbasicdevice">
        <span class="{{.Connectiondotclass}}"></span>
        <p id="title">{{.Title}}</p>
        <p id="tempandhum">{{.Humstatus}}</p>
        <p id="ipaddress">{{.Ipaddress}}</p>
        <p id="portnumber">{{.Portnumber}}</p>
    </div>
{{end}}

那么,你需要像这样包含它(以传递为其预定的参数):
<div id="wrapper" width="100%">
    {{template "sonoffbasicdevice" .SonoffBasic}}
</div>

执行这个“多模板”的操作可能如下所示:
type SonoffBasic struct {
    Connectiondotclass string
    Title              string
    Humstatus          string
    Ipaddress          string
    Portnumber         string
}

type WebPageHTMLItems struct{
    SonoffBasic *SonoffBasic
}

params := &WebPageHTMLItems{
    SonoffBasic: &SonoffBasic{
        Connectiondotclass: "someclass",
        Title:              "sometitle",
        Humstatus:          "somestatus",
        Ipaddress:          "someip",
        Portnumber:         "someport",
    },
}

err := templates.ExecuteTemplate(w, "index.html", params)

感谢您详细解释如何实现我的div。甚至告诉我如何更好地做到这一点,我将首先采用简单的解决方案,以查看HTML页面是否正常工作。然后再使用{{template}}操作进行重写。因为这样可以得到更安全和更美观的代码。 - Roel Balink

0
我们在Golang中有两个生成模板的包。
  1. html/template
  2. text/template
如果您对数据非常确定,可以使用text/template而不是html/template。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接