Go语言的模板引擎拥有一个非常有趣的特性——它可以根据内容所处的上下文改变其显示的内容。是的,你没看错。根据内容在文档中所处的位置,模板在显示这些内容的时候将对其进行相应的修改,换句话说,Go的模板将以上下文感知(context-aware)的方式显示内容。那么这个特性有什么用,我们又会在什么地方用到这个特性呢?
上下文感知的一个显而易见的用途就是对被显示的内容实施正确的转义(escape):这意味着,如果模板显示的是HTML格式的内容,那么模板将对其实施HTML转义;如果模板显示的是JavaScript格式的内容,那么模板将对其实施JavaScript转义;诸如此类。除此之外,Go模板引擎还可以识别出内容中的URL或者CSS样式。代码清单5-17展示了一个上下文感知特性的使用例子。
代码清单5-17 为了展示模板中的上下文感知特性而设置的处理器
package main
import (
"net/http"
"html/template"
)
func process(w http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles("tmpl.html")
content := `I asked: <i>"What's up?"</i>`
t.Execute(w, content)
}
func main() {
server := http.Server{
Addr: "127.0.0.1:8080",
}
http.HandleFunc("/process", process)
server.ListenAndServe()
}
这个处理器向模板发送了文本字符串I asked: <i>"What'sup?"</i>
,它包含了几个需要事先转义的特殊字符,代码清单5-18展示了与这个处理器相对应的模板文件tmpl.html
。
代码清单5-18 上下文感知模板
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Go Web Programming</title>
</head>
<body>
<div>{{ . }}</div>
<div><a href="/{{ . }}">Path</a></div>
<div><a href="/?q={{ . }}">Query</a></div>
<div><a onclick="f('{{ . }}')">Onclick</a></div>
</body>
</html>
正如代码所示,这个模板将传入的参数放到了HTML中的多个不同的位置,并且每个位置都使用了<div>标签对其进行包裹。如果我们使用4.1.4节介绍的方法,通过cURL获取未经改动的原始HTML文件,那么我们将得到以下结果:
curl –i 127.0.0.1:8080/process
HTTP/1.1 200 OK
Date: Sat, 07 Feb 2015 05:42:41 GMT
Content-Length: 505
Content-Type: text/html; charset=utf-8
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Go Web Programming</title>
</head>
<body>
<div>I asked: <i>"What's up?"</i></div>
<div>
<a href="/I%20asked:%20%3ci%3e%22What%27s%20up?%22%3c/i%3e">Path</a>
</div>
<div>
<a href="/?q=I%20asked%3a%20%3ci%3e%22What%27s%20up%3f%22%3c%2fi%3e">Query</a>
</div>
<div>
<a onclick="f('I asked: \x3ci\x3e\x22What\x27s up?\x22\x3c\/i\x3e')">Onclick</a>
</div>
</body>
</html>
这个结果看上去有点儿复杂,表5-1展示了结果HTML与输入原文之间的区别。
表5-1 Go模板中的上下文感知:根据动作所在的位置,同样的内容输入将产生不同的输出结果
上下文感知特性主要用于实现自动的防御编程,并且它使用起来非常方便。通过根据上下文对内容进行修改,Go模板可以防止某些明显并且低级的编程错误。比如,接下来的内容就会向我们展示如何使用上下文感知特性来防御XSS(cross-site scripting,跨站脚本)攻击。