
本文深入探讨了 Go 语言中模板的使用,特别是 template.FormatterMap 的工作原理,解释了为什么需要包装函数来适配特定的函数签名。同时,还探讨了如何修改 HTTP 处理函数以接受命令行参数,从而提高程序的灵活性和可配置性。
理解 template.FormatterMap 和函数签名
在 Go 语言的 html/template 包中,FormatterMap 类型定义了一个字符串到格式化函数的映射。这些格式化函数用于在模板执行期间处理特定类型的数据。FormatterMap 的定义如下:
type FormatterMap map[string]func(io.Writer, interface{}, string)这意味着 FormatterMap 中的每个值都必须是一个具有 func(io.Writer, interface{}, string) 签名的函数。
现在,考虑 template.HTMLEscape 函数的签名:
func HTMLEscape(w io.Writer, b []byte)
这个函数接受一个 io.Writer 和一个字节切片 []byte 作为参数,并将字节切片进行 HTML 转义后写入 io.Writer。
由于 HTMLEscape 的签名与 FormatterMap 所需的签名不匹配,因此不能直接将 HTMLEscape 函数赋值给 FormatterMap 中的一个键。
这就是为什么需要一个包装函数 UrlHtmlFormatter 的原因。这个包装函数接收 io.Writer、interface{} 和 string 作为参数,并在其内部调用 HTMLEscape 函数。通过这种方式,UrlHtmlFormatter 适配了 FormatterMap 所需的函数签名,同时允许我们使用 HTMLEscape 函数来处理模板中的数据。
以下是 UrlHtmlFormatter 函数的示例:
func UrlHtmlFormatter(w io.Writer, v interface{}, fmt string) {
template.HTMLEscape(w, []byte(http.URLEscape(v.(string))))
}在这个函数中,v.(string) 将 interface{} 类型的值转换为字符串。然后,http.URLEscape 函数对字符串进行 URL 编码,并将结果转换为字节切片。最后,template.HTMLEscape 函数对字节切片进行 HTML 转义,并将结果写入 io.Writer。
修改 HTTP 处理函数以接受命令行参数
原始代码中的 QR 函数接受一个 http.Conn 和一个 http.Request 作为参数:
func QR(c *http.Conn, req *http.Request) {
templ.Execute(req.FormValue("s"), c)
}要修改这个函数以接受命令行参数,可以使用 flag 包来定义和解析命令行标志。
SmartB2B 是一款基于PHP、MySQL、Smarty的B2B行业电子商务网站管理系统,系统提供了供求模型、企业模型、产品模型、人才招聘模型、资讯模型等模块,适用于想在行业里取得领先地位的企业快速假设B2B网站,可以运行于Linux与Windows等多重服务器环境,安装方便,使用灵活。 系统使用当前流行的PHP语言开发,以MySQL为数据库,采用B/S架构,MVC模式开发。融入了模型化、模板
首先,在 main 函数中定义一个命令行标志:
var text = flag.String("text", "", "Text to encode in QR code")这个标志名为 text,默认值为空字符串,描述为 "Text to encode in QR code"。
然后,在 main 函数中调用 flag.Parse() 来解析命令行标志:
func main() {
flag.Parse()
http.HandleFunc("/", QR) // 使用 http.HandleFunc
err := http.ListenAndServe(*addr, nil)
if err != nil {
log.Fatal("ListenAndServe:", err) // 使用 log.Fatal
}
}现在,可以修改 QR 函数以使用命令行标志的值:
func QR(w http.ResponseWriter, req *http.Request) {
var data string
if *text != "" {
data = *text
} else {
data = req.FormValue("s")
}
templ.Execute(data, w)
}在这个修改后的 QR 函数中,首先检查命令行标志 text 是否为空。如果不为空,则使用命令行标志的值作为要编码的数据。否则,使用请求参数 s 的值作为要编码的数据。
注意事项:
- 需要将 QR 函数的签名更改为 func(w http.ResponseWriter, req *http.Request),以便与 http.HandleFunc 兼容。
- 需要使用 log.Fatal 而不是 log.Exit,以便在发生错误时正确退出程序。
- 需要使用 http.ResponseWriter 作为 templ.Execute 的第二个参数。
完整代码示例:
package main
import (
"flag"
"fmt"
"html/template"
"io"
"log"
"net/http"
"net/url"
)
var addr = flag.String("addr", ":1718", "http service address") // Q=17, R=18
var text = flag.String("text", "", "Text to encode in QR code")
var fmap = template.FuncMap{
"html": template.HTMLEscapeString,
"url+html": UrlHtmlFormatter,
}
var templ = template.Must(template.New("qr").Funcs(fmap).Parse(templateStr))
func main() {
flag.Parse()
http.HandleFunc("/", QR)
fmt.Println("Server listening on", *addr)
err := http.ListenAndServe(*addr, nil)
if err != nil {
log.Fatal("ListenAndServe:", err)
}
}
func QR(w http.ResponseWriter, req *http.Request) {
var data string
if *text != "" {
data = *text
} else {
data = req.FormValue("s")
}
err := templ.Execute(w, data)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func UrlHtmlFormatter(v interface{}) template.HTML {
escapedURL := url.QueryEscape(fmt.Sprint(v))
escapedHTML := template.HTMLEscapeString(escapedURL)
return template.HTML(escapedHTML)
}
const templateStr = `
QR Link Generator
{{if .}}
@@##@@
{{.|html}}
{{end}}
`总结:
通过理解 template.FormatterMap 的函数签名要求,我们可以创建包装函数来适配不同的函数。此外,使用 flag 包可以轻松地向 Go 程序添加命令行参数,从而提高程序的灵活性和可配置性。









