標準庫之template

BigSun丶發表於2024-03-14

目錄
  • Template
    • 1. 模板
    • 1. 模板示例
    • 2. 模板語法
      • (1){{.}}
      • (2)註釋
      • (3)pipeline
      • (4)變數
    • 3. 條件判斷
      • (1)range
      • (2)with
    • 4. 預定義函式
    • 5. 比較函式
    • 6. 自定義函式
    • 7. 巢狀template

Template

  • html/template包實現了資料驅動的模板,用於生成可對抗程式碼注入的安全HTML輸出。它提供了和text/template包相同的介面,Go語言中輸出HTML的場景都應使用text/template包。
  • 功能同python的Django框架的模板語言類似

1. 模板

  • 在基於MVC的Web架構中,我們通常需要在後端渲染一些資料到HTML檔案中,從而實現動態的網頁效果。

1. 模板示例

  • 透過將模板應用於一個資料結構(即該資料結構作為模板的引數)來執行,來獲得輸出。模板中的註釋引用資料介面的元素(一般如結構體的欄位或者字典的鍵)來控制執行過程和獲取需要呈現的值。模板執行時會遍歷結構並將指標表示為’.‘(稱之為”dot”)指向執行過程中資料結構的當前位置的值。

  • 用作模板的輸入文字必須是utf-8編碼的文字。”Action”—資料運算和控制單位—由”“界定;在Action之外的所有文字都不做修改的複製到輸出中。Action內部不能有換行,但註釋可以有換行。

  • HTML檔案程式碼如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Hello</title>
</head>
<body>
    <p>Hello {{.}}</p>
</body>
</html>

我們的HTTP server端程式碼如下:

// main.go

func sayHello(w http.ResponseWriter, r *http.Request) {
    // 解析指定檔案生成模板物件
    tmpl, err := template.ParseFiles("./hello.html")
    if err != nil {
        fmt.Println("create template failed, err:", err)
        return
    }
    // 利用給定資料渲染模板,並將結果寫入w
    tmpl.Execute(w, "5lmh.com")
}
func main() {
    http.HandleFunc("/", sayHello)
    err := http.ListenAndServe(":9090", nil)
    if err != nil {
        fmt.Println("HTTP server failed,err:", err)
        return
    }
}

2. 模板語法

(1){{.}}

  • 模板語法都包含在{{和}}中間,其中{{.}}中的點表示當前物件。

  • 當我們傳入一個結構體物件時,我們可以根據.來訪問結構體的對應欄位。例如:

// main.go

type UserInfo struct {
    Name   string
    Gender string
    Age    int
}

func sayHello(w http.ResponseWriter, r *http.Request) {
    // 解析指定檔案生成模板物件
    tmpl, err := template.ParseFiles("./hello.html")
    if err != nil {
        fmt.Println("create template failed, err:", err)
        return
    }
    // 利用給定資料渲染模板,並將結果寫入w
    user := UserInfo{
        Name:   "枯藤",
        Gender: "男",
        Age:    18,
    }
    tmpl.Execute(w, user)
}
  • HTML檔案程式碼如下:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Hello</title>
</head>
<body>
    <p>Hello {{.Name}}</p>
    <p>性別:{{.Gender}}</p>
    <p>年齡:{{.Name}}</p>
</body>
</html>
  • 同理,當我們傳入的變數是map時,也可以在模板檔案中透過.根據key來取值。

(2)註釋

    {{/* a comment */}}
    註釋,執行時會忽略。可以多行。註釋不能巢狀,並且必須緊貼分界符始止。

(3)pipeline

  • pipeline是指產生資料的操作。比如{{.}}{{.Name}}等。Go的模板語法中支援使用管道符號|連結多個命令,用法和unix下的管道類似:|前面的命令會將運算結果(或返回值)傳遞給後一個命令的最後一個位置。

  • 注意 : 並不是只有使用了|才是pipeline。Go的模板語法中,pipeline的概念是傳遞資料,只要能產生資料的,都是pipeline。

(4)變數

  • Action裡可以初始化一個變數來捕獲管道的執行結果。初始化語法如下:
    $variable := pipeline
  • 其中$variable是變數的名字。宣告變數的action不會產生任何輸出。

3. 條件判斷

  • Go模板語法中的條件判斷有以下幾種:
{{if pipeline}} T1 {{end}}

{{if pipeline}} T1 {{else}} T0 {{end}}

{{if pipeline}} T1 {{else if pipeline}} T0 {{end}}

(1)range

  • Go的模板語法中使用range關鍵字進行遍歷,有以下兩種寫法,其中pipeline的值必須是陣列、切片、字典或者通道。
{{range pipeline}} T1 {{end}}
如果pipeline的值其長度為0,不會有任何輸出

{{range pipeline}} T1 {{else}} T0 {{end}}
如果pipeline的值其長度為0,則會執行T0。

(2)with

{{with pipeline}} T1 {{end}}
如果pipeline為empty不產生輸出,否則將dot設為pipeline的值並執行T1。不修改外面的dot。

{{with pipeline}} T1 {{else}} T0 {{end}}
如果pipeline為empty,不改變dot並執行T0,否則dot設為pipeline的值並執行T1。

4. 預定義函式

  • 執行模板時,函式從兩個函式字典中查詢:首先是模板函式字典,然後是全域性函式字典。一般不在模板內定義函式,而是使用Funcs方法新增函式到模板裡。

  • 預定義的全域性函式如下:

and
    函式返回它的第一個empty引數或者最後一個引數;
    就是說"and x y"等價於"if x then y else x";所有引數都會執行;
or
    返回第一個非empty引數或者最後一個引數;
    亦即"or x y"等價於"if x then x else y";所有引數都會執行;
not
    返回它的單個引數的布林值的否定
len
    返回它的引數的整數型別長度
index
    執行結果為第一個引數以剩下的引數為索引/鍵指向的值;
    如"index x 1 2 3"返回x[1][2][3]的值;每個被索引的主體必須是陣列、切片或者字典。
print
    即fmt.Sprint
printf
    即fmt.Sprintf
println
    即fmt.Sprintln
html
    返回其引數文字表示的HTML逸碼等價表示。
urlquery
    返回其引數文字表示的可嵌入URL查詢的逸碼等價表示。
js
    返回其引數文字表示的JavaScript逸碼等價表示。
call
    執行結果是呼叫第一個引數的返回值,該引數必須是函式型別,其餘引數作為呼叫該函式的引數;
    如"call .X.Y 1 2"等價於go語言裡的dot.X.Y(1, 2);
    其中Y是函式型別的欄位或者字典的值,或者其他類似情況;
    call的第一個引數的執行結果必須是函式型別的值(和預定義函式如print明顯不同);
    該函式型別值必須有1到2個返回值,如果有2個則後一個必須是error介面型別;
    如果有2個返回值的方法返回的error非nil,模板執行會中斷並返回給呼叫模板執行者該錯誤;

5. 比較函式

  • 布林函式會將任何型別的零值視為假,其餘視為真。

  • 下面是定義為函式的二元比較運算的集合:

    eq      如果arg1 == arg2則返回真
    ne      如果arg1 != arg2則返回真
    lt      如果arg1 < arg2則返回真
    le      如果arg1 <= arg2則返回真
    gt      如果arg1 > arg2則返回真
    ge      如果arg1 >= arg2則返回真
  • 為了簡化多引數相等檢測,eq(只有eq)可以接受2個或更多個引數,它會將第一個引數和其餘引數依次比較,返回下式的結果:
    {{eq arg1 arg2 arg3}}
  • 比較函式只適用於基本型別(或重定義的基本型別,如”type Celsius float32”)。但是,整數和浮點數不能互相比較。

6. 自定義函式

  • Go的模板支援自定義函式。
func sayHello(w http.ResponseWriter, r *http.Request) {
    htmlByte, err := ioutil.ReadFile("./hello.html")
    if err != nil {
        fmt.Println("read html failed, err:", err)
        return
    }
    // 自定義一個夸人的模板函式
    kua := func(arg string) (string, error) {
        return arg + "真帥", nil
    }
    // 採用鏈式操作在Parse之前呼叫Funcs新增自定義的kua函式
    tmpl, err := template.New("hello").Funcs(template.FuncMap{"kua": kua}).Parse(string(htmlByte))
    if err != nil {
        fmt.Println("create template failed, err:", err)
        return
    }

    user := UserInfo{
        Name:   "枯藤",
        Gender: "男",
        Age:    18,
    }
    // 使用user渲染模板,並將結果寫入w
    tmpl.Execute(w, user)
}
  • 我們可以在模板檔案hello.html中使用我們自定義的kua函式了。
    {{kua .Name}}

7. 巢狀template

  • 我們可以在template中巢狀其他的template。這個template可以是單獨的檔案,也可以是透過define定義的template。

  • 舉個例子: t.html檔案內容如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>tmpl test</title>
</head>
<body>

    <h1>測試巢狀template語法</h1>
    <hr>
    {{template "ul.html"}}
    <hr>
    {{template "ol.html"}}
</body>
</html>

{{ define "ol.html"}}
<h1>這是ol.html</h1>
<ol>
    <li>吃飯</li>
    <li>睡覺</li>
    <li>打豆豆</li>
</ol>
{{end}}
  • ul.html檔案內容如下:
<ul>
    <li>註釋</li>
    <li>日誌</li>
    <li>測試</li>
</ul>
  • 我們註冊一個templDemo路由處理函式.
http.HandleFunc("/tmpl", tmplDemo)
  • tmplDemo函式的具體內容如下:
func tmplDemo(w http.ResponseWriter, r *http.Request) {
    tmpl, err := template.ParseFiles("./t.html", "./ul.html")
    if err != nil {
        fmt.Println("create template failed, err:", err)
        return
    }
    user := UserInfo{
        Name:   "枯藤",
        Gender: "男",
        Age:    18,
    }
    tmpl.Execute(w, user)
}

相關文章