[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 的歷史教訓可知模板語言應該要回歸其原本的角色,而非把所有的程式邏輯都塞在模板中。

    【分享本文】
    Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email
    【追蹤新文章】
    Facebook Twitter Plurk