位元詩人 [Golang] 網頁設計教學:用模板語言 (Template Language) 將資料寫入網頁

Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email

前言

在網頁程式中使用模板語言 (template language) 可以簡化生成網頁的任務。透過模板和資料的結合,我們可以動態地用程式產生頁面。網頁框架幾乎都會使用模板語言,但現在的模板語言通常會比通用型程式語言來得簡單得多;早期程式人濫用 PHP 造成「義大利麵」程式碼,我們不應該重覆這個反模式 (anti-pattern)。

Go 的模板語言的語法記錄在 text/template 套件,但實際要使用時,要使用 html/template 套件,而不是使用原本的 text/template 套件,因 http/template 套件會對程式碼插入 (code injection) 等情形進行相對應的防護,而 text/template 套件則沒有防護惡意程式碼的功能。

本文會介紹幾個常見的模板語言語法。

變數 (Variable)

由於本範例檔案較多,我們將範例放在這裡,讓讀者追蹤程式碼,這裡僅節錄部分內容。

我們先來看模板的部分:

<!DOCTYPE html>
<html>
<head>
    <title>{{ .Title }}</title>
</head>
<body>
    <h1>{{ .Title }}</h1>
</body>
</html>

我們的目標是將變數 {{ .Title }} 的地方代換掉。來看一下模板相關的程式碼:

func index(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
    var tmpl = template.Must(template.ParseFiles("views/index.html"))

    tmpl.Execute(w, struct {
        Title string
    }{
        "My Awesome Site",
    })
}

在此處,我們用匿名結構 (anonymous struct) 帶入變數,這在 Go 語言中是一個常見的手法。以本例來說,{{ .Title }} 最後會代換成 "My Awesome Site"

程式執行結果如下:

在 Golang 的 html/template 套件中使用變數

if 敘述

我們將完整程式碼放在這裡,有需要的讀者可自行追蹤。

我們先來看模板的部分:

<!DOCTYPE html>
<html>
<head>
    <title>Using if in html/template</title>
</head>
<body>
    {{ if .Lang }}
        <p>My favorite language is {{.Lang}}</p>
    {{ end }}
</body>
</html>

要注意 Go 模板語言的 if 無法做出複雜的判斷式,僅能判斷變數的真偽。這是因為模板語言的目標只是要帶入資料,而非用來建立複雜的程式邏輯。

接著來看程式碼的部分:

func index(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
    var tmpl = template.Must(template.ParseFiles("views/index.html"))

    tmpl.Execute(w, struct {
        Lang string
    }{
        "Golang",
    })
}

這部分同樣是帶入變數,沒什麼特別的地方。

程式執行結果如下:

在 Golang 的 html/template 套件中使用 if 敘述

range 敘述

range 是用來走訪容器 (collection) 的語法。同樣地,我們將完整的範例放在這裡,有需要的讀者可自行追蹤程式碼。

模板的部分如下:

<!DOCTYPE html>
<html>
<head>
    <title>Using range in html/template</title>
</head>
<body>
    <p>Major tier languages for web programming:</p>
    <ul>
        {{ range .Langs }}
        <li>{{ . }}</li>
        {{ end }}
    </ul>
</body>
</html>

容器的元素會將 {{ . }} 的部分代換掉。用 range 做動態的網頁清單是常見的手法。

接著來看程式碼的部分:

func index(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
    var tmpl = template.Must(template.ParseFiles("views/index.html"))

    tmpl.Execute(w, struct {
        Langs []string
    }{
        []string{"Python", "Ruby", "PHP", "Java", "Golang"},
    })
}

在本範例中,我們的變數 Langs 所指向的資料型態為字串陣列,所以可在模板進行迭代。

程式執行的結果如下:

在 Golang 的 html/template 套件中使用 range 敘述

搭配映射 (Map) 的 range 敘述

在上一節中,我們用陣列搭配 range,在本例中,我們改用映射來搭配。我們將完整的範例放在這裡,需要的讀者可自行追蹤程式碼。

模板的部分如下:

<!DOCTYPE html>
<html>
<head>
    <title>Using range with map in html/template</title>
</head>
<body>
    <p>Major web frameworks for web programming:</p>
    <ul>
        {{range $key, $value := .Frameworks}}
        <li>{{ $key }} ({{ $value }})</li>
        {{end}}
    </ul>
</body>
</html>

在這個模板中,我們帶入新的變數 $key$value,在 range 區塊中就可以使用。

如果是陣列,帶入新變數的方式如下:

{{ range $index, $element := .Array }}

  {{/* Use the variables here */}}

{{ end }}

接著來看程式碼的部分:

func index(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
    var tmpl = template.Must(template.ParseFiles("views/index.html"))

    tmpl.Execute(w, struct {
        Frameworks map[string]string
    }{
        map[string]string{
            "Django":  "Python",
            "Rails":   "Ruby",
            "Laravel": "PHP",
            "Spring":  "Java",
            "Gin":     "Golang",
        },
    })
}

在此處,變數 Frameworks 指向的資料型態為映射,該映射的鍵和值皆為字串。帶入的映射可在模板中迭代。

程式執行結果如下:

在 Golang 的 html/template 套件中使用 range 敘述

結語

在本文中,我們介紹了 Go 網頁程式的模板語言,應該足以開始撰寫靜態網頁。

現在的模板語言不會像 PHP 那麼複雜,因為程式人從 PHP 的歷史教訓可知模板語言應該要回歸其原本的角色,而非把所有的程式邏輯都塞在模板中。

關於作者

身為資訊領域碩士,位元詩人 (ByteBard) 認為開發應用程式的目的是為社會帶來價值。如果在這個過程中該軟體能成為永續經營的項目,那就是開發者和使用者雙贏的局面。

位元詩人喜歡用開源技術來解決各式各樣的問題,但必要時對專有技術也不排斥。閒暇之餘,位元詩人將所學寫成文章,放在這個網站上和大家分享。