Go Web 編程中的模板庫(kù)應(yīng)用指南(超詳細(xì))
如果你有過(guò)Web編程的經(jīng)驗(yàn),那么或多或少都聽(tīng)說(shuō)過(guò)或者使用過(guò)模板。簡(jiǎn)而言之,模板是可用于創(chuàng)建動(dòng)態(tài)內(nèi)容的文本文件。例如,你有一個(gè)網(wǎng)站導(dǎo)航欄的模板,其中動(dòng)態(tài)內(nèi)容的一部分可能是根據(jù)當(dāng)前用戶是否登錄顯示登錄還是退出按鈕。
Go提供了兩個(gè)模板庫(kù) text/template和 html/template。這兩個(gè)模板庫(kù)的使用方式是相同的,但是 html/template包在渲染頁(yè)面模板時(shí)會(huì)在后臺(tái)進(jìn)行一些編碼以幫助防止造成代碼注入(XSS 攻擊)。
因?yàn)閮蓚€(gè)模板庫(kù)都使用相同的接口,因此本文中介紹的所有內(nèi)容均可用于這兩個(gè)程序包,但是大多數(shù)時(shí)候我們都會(huì)使用 html/template程序包來(lái)生成HTML代碼段。
模板文件的后綴名
模板文件可以使用 .html或任何其他擴(kuò)展名。但是通常我們將使用 .gohtml擴(kuò)展名來(lái)命名模板文件,因?yàn)榫庉嬈魍ǔJ褂盟鼇?lái)表示你想要高亮 GoHTML模板語(yǔ)法。Atom和 SublimeText等編輯器都具有 Go插件,來(lái)默認(rèn)識(shí)別此擴(kuò)展名。
模板語(yǔ)法
我們先來(lái)創(chuàng)建一個(gè)簡(jiǎn)單的模板文件 test.gohtml:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Go Web</title>
</head>
<body>
{{ . }}
</body>
</html>
{{ 和 }} 中間的半角句號(hào) . 它代表模板對(duì)象執(zhí)行 Execute(w,data)傳入模板的數(shù)據(jù),它是頂級(jí)作用域范圍內(nèi)的,根據(jù)傳入的數(shù)據(jù)不同渲染不同的內(nèi)容。. 可以代表 Go語(yǔ)言中的任何類型,如結(jié)構(gòu)體、 Map等。
在寫模板的時(shí)候,會(huì)經(jīng)常用到 .。比如 {{.}}、 {{len.}}、 {{.Name}}、 {{$x.Name}}
{{ 和 }} 包裹的內(nèi)容統(tǒng)稱為 action,分為兩種類型:
數(shù)據(jù)求值(data evaluations)
控制結(jié)構(gòu)(control structures)
action求值的結(jié)果會(huì)直接復(fù)制到模板中,控制結(jié)構(gòu)和我們寫 Go程序差不多,也是條件語(yǔ)句、循環(huán)語(yǔ)句、變量、函數(shù)調(diào)用等等...模板中的 action 并不多,我們一個(gè)一個(gè)看。
注釋
{{/* comment */}}
裁剪空字符
注意裁剪的是替換內(nèi)容前面或者后面的空字符,你可以理解成模板中{{前面或}}后面的空字符(包括換行符、制表符、空格等)。
// 裁剪 content 前后的空字符
{{- content -}}
// 裁剪 content 前面的空字符
{{- content }}
// 裁剪 content 后面的空字符
{{ content -}}
文本輸出
{{ pipeline }}
pipeline代表的數(shù)據(jù)會(huì)產(chǎn)生與調(diào)用 fmt.Print 函數(shù)類似的輸出,例如整數(shù)類型的 3 會(huì)轉(zhuǎn)換成字符串 "3" 輸出。
條件語(yǔ)句
{{ if pipeline }} T1 {{ end }}
{{ if pipeline }} T1 {{ else }} T0 {{ end }}
{{ if pipeline }} T1 {{ else if pipeline }} T0 {{ end }}
// 上面的語(yǔ)法其實(shí)是下面的簡(jiǎn)寫
{{ if pipeline }} T1 {{ else }}{{ if pipeline }} T0 { {end }}{{ end }}
{{ if pipeline }} T1 {{ else if pipeline }} T2 {{ else }} T0 {{ end }}
如果 pipeline 的值為空,不會(huì)輸出 T1,除此之外 T1 都會(huì)被輸出。
空值有 false、 0、 nil空字符串 ""(長(zhǎng)度為 0 的字符串)。
循環(huán)語(yǔ)句
{{ range pipeline }} T1 {{ end }}
// 這個(gè) else 比較有意思,如果 pipeline 的長(zhǎng)度為 0 則輸出 else 中的內(nèi)容
{{ range pipeline }} T1 {{ else }} T0 {{ end }}
// 獲取容器的下標(biāo)
{{ range $index, $value := pipeline }} T1 {{ end }}
循環(huán)語(yǔ)句中的 pipeline 的值必須是數(shù)組、切片、字典和通道中的一種,即可迭代類型的值,根據(jù)值的長(zhǎng)度輸出多個(gè) T1。
define
{{ define "name" }} T {{ end }}
定義命名為 name 的模板。
template
{{ template "name" }}
{{ template "name" pipeline }}
第一種是直接執(zhí)行名為 name的模板,模板的全局?jǐn)?shù)據(jù)對(duì)象 .設(shè)置為 nil。第二種是點(diǎn) .設(shè)置為pipeline的值,并執(zhí)行名為 name的模板。
block
{{ block "name" pipeline }} T1 {{ end }}
block 的語(yǔ)義是如果有命名為 name 的模板,就引用過(guò)來(lái)執(zhí)行,如果沒(méi)有命名為 name 的模板,就是執(zhí)行自己定義的內(nèi)容。換句話說(shuō),block可以認(rèn)為是設(shè)置一個(gè)默認(rèn)模板。
with
{{ with pipeline }} T1 {{ end }}
// 如果 pipeline 是空值則輸出 T0
{{ with pipeline }} T1 {{ else }} T0 {{ end }}
{{ with arg }}
. // 此時(shí) . 就是 arg
{{ end }}
with 創(chuàng)建一個(gè)新的上下文環(huán)境,在此環(huán)境中的 . 與外面的 . 無(wú)關(guān)。
對(duì)于第一種格式,當(dāng)pipeline不為0值的時(shí)候,點(diǎn) .設(shè)置為pipeline運(yùn)算的值,否則跳過(guò)。對(duì)于第二種格式,當(dāng)pipeline為0值時(shí),執(zhí)行else語(yǔ)句塊,否則 .設(shè)置為pipeline運(yùn)算的值,并執(zhí)行T1。
例如:
{{with .Person}}{{ .Name}}{{end}}
在這個(gè) with 塊中 .Name實(shí)際上引用的是全局?jǐn)?shù)據(jù)對(duì)象的 .Person.Name。
實(shí)踐練習(xí):課程花名冊(cè)頁(yè)面
了解完模板語(yǔ)法后,接下來(lái)讓我們?cè)?http_demo項(xiàng)目中結(jié)合 BootStrap創(chuàng)建一個(gè)簡(jiǎn)單的模板,來(lái)展示服務(wù)器如何把數(shù)據(jù)傳遞給模板、渲染 HTML頁(yè)面,把頁(yè)面響應(yīng)返回給客戶端。
我們創(chuàng)建一個(gè)用來(lái)展示大學(xué)物理課程的花名冊(cè)(授課老師和上課學(xué)生)
創(chuàng)建頁(yè)面模板
首先在我們的項(xiàng)目添加一個(gè) views目錄用于存放模板文件,在創(chuàng)建三個(gè)模板文件分別是:
layout.gohtml 用于存放頁(yè)面的整體布局。
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<title>Bootstrap Template Page for Go Web Programming</title>
<!-- Bootstrap core CSS -->
<link rel="external nofollow" rel="stylesheet">
</head>
<body>
{{ template "nav" .}}
<div class="container">
{{template "content" .}}
</div>
<script src="http://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</body>
</html>
nav.gohtml是網(wǎng)頁(yè)頭部區(qū)域的頁(yè)面模板。
{{define "nav"}}
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >Person general infor</a>
</div>
</div>
</nav>
<div class="jumbotron">
<div class="container">
<h1>Hello, Professor {{.Teacher.Name}}</h1>
<ul>
<li>Name : {{.Teacher.Name}}<p>
<li>Subject : {{.Teacher.Subject}}
</ul>
<p><a class="btn btn-primary btn-lg" href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" role="button">More »</a></p>
</div>
</div>
{{end}}
content.gohtml是網(wǎng)頁(yè)主體內(nèi)容部分的頁(yè)面模板。
{{define "content"}}
{{range .Students}}
<div class="row">
<div class="col-md-4">
<h2>Name</h2>
<p>Name has the value of : {{.Name}} </p>
<p><a class="btn btn-default" href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" role="button">More »</a></p>
</div>
<div class="col-md-4">
<h2>Id</h2>
<p>Id has the value of : {{.Id}} </p>
<p><a class="btn btn-default" href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" role="button">More »</a></p>
</div>
<div class="col-md-4">
<h2>Country</h2>
<p>Country has the value of : {{.Country}} </p>
<p><a class="btn btn-default" href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" role="button">More »</a></p>
</div>
</div>
{{end}}
{{end}}
在 layout.gohtml中我們引用了另外的兩個(gè)模板:
{{ template "nav" .}}
{{template "content" .}}
這樣不同的頁(yè)面變化的部分就只是 content部分,針對(duì)不同的頁(yè)面我們只需要定義多個(gè) content模板,每次根據(jù)不同請(qǐng)求使用不同的 content模板就行了。當(dāng)然這里的例子有點(diǎn)簡(jiǎn)陋,大家理解意思就行了。
注意模板名稱后面的 .,我們把 layout.gohtml的全局?jǐn)?shù)據(jù)對(duì)象傳給了另外兩個(gè)模板這樣,在子模板里也能訪問(wèn)傳給模板的數(shù)據(jù)了。如果頁(yè)面模板中使用的數(shù)據(jù)字段和循環(huán)語(yǔ)句有點(diǎn)疑惑可以先不用管,繼續(xù)往下看,等看過(guò)傳給頁(yè)面模板的數(shù)據(jù)后自然就理解了。
創(chuàng)建響應(yīng)頁(yè)面請(qǐng)求的Handler
接下來(lái)創(chuàng)建一個(gè)伺服頁(yè)面請(qǐng)求的 Handler:
package handler
import (
"fmt"
"html/template"
"net/http"
)
type Teacher struct {
Name string
Subject string
}
type Student struct {
Id int
Name string
Country string
}
type Rooster struct {
Teacher Teacher
Students []Student
}
func ShowIndexView(response http.ResponseWriter, request *http.Request) {
teacher := Teacher{
Name: "Alex",
Subject: "Physics",
}
students := []Student{
{Id: 1001, Name: "Peter", Country: "China"},
{Id: 1002, Name: "Jeniffer", Country: "Sweden"},
}
rooster := Rooster{
Teacher: teacher,
Students: students,
}
tmpl, err := template.ParseFiles("./views/layout.gohtml",
"./views/nav.gohtml",
"./views/content.gohtml")
if err != nil {
fmt.Println("Error " + err.Error())
}
tmpl.Execute(response, rooster)
}
使用 template.ParseFiles加載這個(gè)頁(yè)面要使用的全部三個(gè)模板(如果加載少了,訪問(wèn)頁(yè)面時(shí)會(huì)發(fā)生 panic),然后使用模板對(duì)象的 Execute方法把我們存儲(chǔ)了花名冊(cè)信息的數(shù)據(jù)對(duì)象傳給模板: tmpl.Execute(response,rooster) 渲染頁(yè)面并寫到響應(yīng)里去( http.ResponseWriter對(duì)象)。
注冊(cè)頁(yè)面路由
處理程序?qū)懲旰螅瑸槠渥?cè)路由,在我們項(xiàng)目的路由模塊添加如下路由:
package router
import (
"example.com/http_demo/middleware"
"github.com/gorilla/mux"
"example.com/http_demo/handler"
)
func RegisterRoutes(r *mux.Router) {
r.Use(middleware.Logging())
...
viewRouter := r.PathPrefix("/view").Subrouter()
viewRouter.HandleFunc("/index", handler.ShowIndexView)
}
訪問(wèn)頁(yè)面
現(xiàn)在所有步驟都完成了,重啟我們的服務(wù)器后就可以訪問(wèn)到新寫的頁(yè)面了。
如果是在本地電腦里,用 Ctrl+C結(jié)束服務(wù)器進(jìn)程后再次執(zhí)行 go run main.go。如果是使用我們之前文章里的 Docker開發(fā)環(huán)境的話,需要在 docker-compose.yml所在的目錄里用 docker-compose restart重啟服務(wù)。
打開瀏覽器輸入 http://localhost:8000/view/index就能訪問(wèn)到我們剛才寫的頁(yè)面了。

總結(jié)
今天的文章講解了 Go模板最常使用的幾個(gè)功能的使用方法,使用 html/template模板庫(kù)結(jié)合 BootStrap做頁(yè)面模板,還是比較簡(jiǎn)單的 BootStrap幫我們解決了很多前端的樣式問(wèn)題。模板庫(kù)還有很多更高級(jí)的用法,比如在模板中調(diào)用函數(shù)、定義變量等功能,可以看下文末給出的參考鏈接了解這部分內(nèi)容。在前后端分離架構(gòu)流行的今天我覺(jué)得作為用 Go開發(fā)的后端工程師了解文章中列出的這些功能就夠了。
今天的例子中是通過(guò) CDN 引用的 BootStrap靜態(tài)資源,到目前我們的服務(wù)器還無(wú)法伺服靜態(tài)資源,這個(gè)我們下篇文章再講。
到此這篇關(guān)于Go Web 編程中的模板庫(kù)應(yīng)用指南(超詳細(xì))的文章就介紹到這了,更多相關(guān)go web 編程模塊庫(kù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
在Linux系統(tǒng)中安裝Go語(yǔ)言的詳細(xì)教程
這篇文章主要介紹了在Linux系統(tǒng)中安裝Go語(yǔ)言的詳細(xì)教程,由于國(guó)內(nèi)很多人對(duì)谷歌的盲目追捧,導(dǎo)致Go語(yǔ)言在國(guó)內(nèi)的人氣遠(yuǎn)超國(guó)外...需要的朋友可以參考下2015-06-06
go語(yǔ)言處理TCP拆包/粘包的具體實(shí)現(xiàn)
TCP的拆包/粘包也算是網(wǎng)絡(luò)編程中一個(gè)比較基礎(chǔ)的問(wèn)題了,本文主要介紹了go語(yǔ)言處理TCP拆包/粘包,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-12-12
淺談Golang 切片(slice)擴(kuò)容機(jī)制的原理
我們知道 Golang 切片在容量不足的情況下會(huì)進(jìn)行擴(kuò)容,擴(kuò)容的原理是怎樣的呢,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-06-06
Golang根據(jù)job數(shù)量動(dòng)態(tài)控制每秒?yún)f(xié)程的最大創(chuàng)建數(shù)量方法詳解
這篇文章主要介紹了Golang根據(jù)job數(shù)量動(dòng)態(tài)控制每秒?yún)f(xié)程的最大創(chuàng)建數(shù)量方法2024-01-01
Go語(yǔ)言遍歷map實(shí)現(xiàn)(訪問(wèn)map中的每一個(gè)鍵值對(duì))
這篇文章主要介紹了Go語(yǔ)言遍歷map實(shí)現(xiàn)(訪問(wèn)map中的每一個(gè)鍵值對(duì)),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01
使用GO實(shí)現(xiàn)Paxos共識(shí)算法的方法
這篇文章主要介紹了使用GO實(shí)現(xiàn)Paxos共識(shí)算法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作,具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09
Go?實(shí)戰(zhàn)單隊(duì)列到優(yōu)先級(jí)隊(duì)列實(shí)現(xiàn)圖文示例
這篇文章主要為大家介紹了Go?實(shí)戰(zhàn)單隊(duì)列到優(yōu)先級(jí)隊(duì)列圖文示例實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07

