前言
我們先前的範例皆回傳純文字頁面,但實務上我們會回傳 HTML 頁面給使用者觀看,或是回傳 XML (或 JSON) 文件來傳輸資料。本文說明在網頁程式中加入 HTML 頁面的方式。
(反模式) 直接嵌入網頁
HTML 文件在本質上是加了標籤 (tag) 的文字文件,所以我們可以直接把整個 HTML 文件當成是一個多行字串輸出。按照這個想法寫出以下 (不良的) 程式碼:
package main
import (
"github.com/julienschmidt/httprouter"
"net/http"
)
func main() {
mux := httprouter.New()
mux.GET("/", index)
server := http.Server{
Addr: "127.0.0.1:8080",
Handler: mux,
}
server.ListenAndServe()
}
func index(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
// NEVER DO THIS IN PRODUCTION CODE
str := `<!DOCTYPE html>
<html>
<head><title>Hello, HTML</title></head>
<body><h1>Hello, HTML</h1><p>Add more text here</p></body>
</html>
`
w.Write([]byte(str))
}
實務上,我們當然不會這樣做。早期的 CGI 基本上就是用這個思維在寫程式,這套模式已經算是時代的眼淚,我們不需要重覆這樣的舊路線。
使用模板 (Template)
在實務上,我們會利用模板 (template) 將內容和版面進行初步的分離。模板是網頁的骨架,會預先寫好一些和板面相關的代碼,要使用模板時再填入原本尚未完成的內容即可。透過模板引擎 (template engine) 的處理,模板和填入的資料會合併後轉為頁面。理論上所有文字格式的內容都能透過這種模式輸出。
在 Golang 網頁程式中,我們通常拿模板來輸出 HTML 頁面。XML 和 JSON 文件則會用相關函式庫來産生,不會直接用模板去生 XML 或 JSON 文件。
以本例來說,我們在 views/index.html 加入以下模板:
<!DOCTYPE html>
<html>
<head>
<title>{{ . }}</title>
</head>
<body>
<p>{{ . }}</p>
</body>
</html>
在此模板中,{{ . }}
部分即是挖空的內容,我們可以依需求填入不同的內容。
接著來看主程式的部分:
package main
import (
"github.com/julienschmidt/httprouter"
"html/template"
"net/http"
)
func main() {
mux := httprouter.New()
mux.GET("/", index)
server := http.Server{
Addr: "127.0.0.1:8080",
Handler: mux,
}
server.ListenAndServe()
}
func index(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
t := template.Must(template.ParseFiles("views/index.html"))
t.Execute(w, "Hello, Template")
}
本程式的關鍵在於建立和使用模板,我們稍微說明一下。如果想查 API 的使用方式,請查閱 http/template 的文件。
使用 template.Must 可以建立模板,其函式原型如下:
func template.Must(tmpl, error) *tmpl
但我們的範例是這樣:
template.Must(template.ParseFiles("views/index.html"))
如果看範例,會以為我們只傳入一個參數。但實際上我們傳入兩個參數,因為 template.ParseFiles
即會回傳兩個參數,所以可以滿足 template.Must
的 API。會有這樣的現象是因為 template.Must
是設計來搭配其他函式,以簡化建立模板的過程。
接著來看以 Execute 函式生成模板的函式原型:
func tmpl.Execute(out, data) err
out
是輸出的目標,在本範例是 http.ResponseWriter。data
利用空介面的特性,可視需求傳入不同的東西。理論上要檢查是否引發錯誤,但本範例為了簡化程式碼,省略這個步驟。
結語
在本文中,我們以簡短的範例來展示如何在 Golang 網頁程式中使用模板。在後續的文章中,我們會介紹 Golang 的模板的語法。