让我们以一个完整的Go程序示例 —— 一个web服务 —— 来作为这篇文档的结尾。事实上,这个例子其实是一类“Web re-server”,也就是说它其实是对另一个Web服务的封装。谷歌公司提供了一个用来自动将数据格式化为图表或图形的在线服务,其网址是:[http://chart.apis.google.com](http://chart.apis.google.com/)。 这个服务使用起来其实有点麻烦 —— 你需要把数据添加到URL中作为请求参数,因此不易于进行交互操作。我们现在的这个程序会为用户提供一个更加友好的界面来处理某种形式的数据:对于给定的 一小段文本数据,该服务将调用图标在线服务来产生一个QR码,它用一系列二维方框来编码文本信息。可以用手机摄像头扫描该QR码并进行交互操作,比如将 URL地址编码成一个QR码,你就省去了往手机里输入这个URL地址的时间。
下面是完整的程序代码,后面会给出详细的解释。
~~~
package main
import (
"flag"
"html/template"
"log"
"net/http"
)
var addr = flag.String("addr", ":1718", "http service address") // Q=17, R=18
var templ = template.Must(template.New("qr").Parse(templateStr))
func main() {
flag.Parse()
http.Handle("/", http.HandlerFunc(QR))
err := http.ListenAndServe(*addr, nil)
if err != nil {
log.Fatal("ListenAndServe:", err)
}
}
func QR(w http.ResponseWriter, req *http.Request) {
templ.Execute(w, req.FormValue("s"))
}
const templateStr = `
<html>
<head>
<title>QR Link Generator</title>
</head>
<body>
{{if .}}
<img src="http://chart.apis.google.com/chart?chs=300x300&cht=qr&choe=UTF-8&chl={{.}}" />
<br>
{{.}}
<br>
<br>
{{end}}
<form action="/" name=f method="GET"><input maxLength=1024 size=70
name=s value="" title="Text to QR Encode"><input type=submit
value="Show QR" name=qr>
</form>
</body>
</html>
`
~~~
`main`函数之前的部分很容易理解。包flag用来构建我们这个服务默认的HTTP端口。从模板变量`templ`开始进入了比较好玩的部分,它的功能是用来构建一个HTML模板,该模板被我们的服务器处理并用来显式页面信息;我们后面还会看到更多细节。
`main`函数使用我们之前介绍的机制来解析flag,并将函数`QR`绑定到我们服务的根路径。然后调用`http.ListenAndServe`方法启动服务;该方法将在服务器运行过程中一直处于阻塞状态。
`QR`函数用来接收包含格式化数据的请求信息,并以该数据`s`为参数对模板进行实例化操作。
模板包`html/template`的功能非常强大;上述程序仅仅触及其冰山一角。本质上说,它会根据传入`templ.Execute`方法的参数,在本例中是格式化数据,在后台替换相应的元素并重新生成HTML文本。在模板文本(`templateStr`)中,双大括号包裹的区域意味着需要进行模板替换动作。在`{{if .}}`和`{{end}}`之间的部分只有在当前数据项,也就是`.`,不为空时才被执行。也就是说,如果对应字符串为空,内部的模板信息将被忽略。
代码片段`{{.}}`表示在页面中显示传入模板的数据 —— 也就是查询字符串本身。HTML模板包会自动提供合适的处理方式,使得文本可以安全的显示。
模板串的剩余部分就是将被加载显示的普通HTML文本。如果你觉得这个解释太笼统了,可以进一步参考[Go文档](http://localhost:6060/pkg/html/template/)中,关于模板包的深入讨论。
看,仅仅用了很少量的代码加上一些数据驱动的HTML文本,你就搞定了一个很有用的web服务。这就是Go语言的牛X之处:用很少的一点代码就能实现很强大的功能。