Go Web下gin框架的模板渲染的實(shí)現(xiàn)
〇、前言
Gin框架是一個(gè)用于構(gòu)建Web應(yīng)用程序的輕量級(jí)Web框架,使用Go語(yǔ)言開(kāi)發(fā)。它具有高性能、低內(nèi)存占用和快速路由匹配的特點(diǎn),旨在提供簡(jiǎn)單、快速的方式來(lái)開(kāi)發(fā)可擴(kuò)展的Web應(yīng)用程序。
Gin框架的設(shè)計(jì)目標(biāo)是保持簡(jiǎn)單和易于使用,同時(shí)提供足夠的靈活性和擴(kuò)展性,使開(kāi)發(fā)人員能夠根據(jù)項(xiàng)目的需求進(jìn)行定制。它提供了許多有用的功能,如中間件支持、路由組、請(qǐng)求參數(shù)解析、模板渲染等,使開(kāi)發(fā)人員能夠快速構(gòu)建高效的Web應(yīng)用程序。
以下是Gin框架的一些主要特性:
快速的性能:Gin框架采用了高性能的路由引擎,使請(qǐng)求路由匹配變得非??焖?。
中間件支持:Gin框架支持中間件機(jī)制,允許你在請(qǐng)求處理過(guò)程中添加自定義的中間件,用于處理認(rèn)證、日志記錄、錯(cuò)誤處理等功能。
路由組:Gin框架允許將路由按照邏輯組織成路由組,使代碼結(jié)構(gòu)更清晰,并且可以為不同的路由組添加不同的中間件。
請(qǐng)求參數(shù)解析:Gin框架提供了方便的方法來(lái)解析請(qǐng)求中的參數(shù),包括查詢字符串參數(shù)、表單參數(shù)、JSON參數(shù)等。
模板渲染:雖然Gin框架本身不提供模板引擎,但它與多種模板引擎庫(kù)(如html/template、pongo2等)集成,使你能夠方便地進(jìn)行模板渲染。
錯(cuò)誤處理:Gin框架提供了內(nèi)置的錯(cuò)誤處理機(jī)制,可以捕獲和處理應(yīng)用程序中的錯(cuò)誤,并返回適當(dāng)?shù)腻e(cuò)誤響應(yīng)。
總體而言,Gin框架是一個(gè)簡(jiǎn)單、輕量級(jí)但功能強(qiáng)大的Web框架,非常適合構(gòu)建高性能、可擴(kuò)展的Web應(yīng)用程序。它在Go語(yǔ)言社區(qū)中得到廣泛的認(rèn)可,并被許多開(kāi)發(fā)人員用于構(gòu)建各種類型的Web應(yīng)用程序。
一、html/template
html/template是Go語(yǔ)言標(biāo)準(zhǔn)庫(kù)中的一個(gè)包,用于生成和渲染HTML模板。它提供了一種安全且靈活的方式來(lái)生成HTML輸出,支持模板繼承、變量替換、條件語(yǔ)句、循環(huán)結(jié)構(gòu)等功能。
html/template包的主要目標(biāo)是防止常見(jiàn)的Web安全漏洞,如跨站腳本攻擊(XSS)。它通過(guò)自動(dòng)進(jìn)行HTML轉(zhuǎn)義和編碼來(lái)確保生成的HTML是安全的,并防止惡意用戶注入惡意代碼。
使用html/template包可以將動(dòng)態(tài)數(shù)據(jù)與靜態(tài)HTML模板分離,使代碼更易于維護(hù)和重用。你可以定義模板文件,然后將數(shù)據(jù)傳遞給模板進(jìn)行渲染,最后生成最終的HTML輸出。
(一)初次渲染
先創(chuàng)建一個(gè)名為 hello.tmpl的文件:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<title>Hello</title>
</head>
<body>
<p>hello, {{.}}</p>
</body>
</html>
這里的{{.}}中的.就代表了我們要填充的東東,接著創(chuàng)建我們的main.go函數(shù):
package main
import (
"fmt"
"html/template"
"net/http"
)
func sayHello(w http.ResponseWriter, r *http.Request) {
// 請(qǐng)勿刻舟求劍,用絕對(duì)地址
t, err := template.ParseFiles("/Users/luliang/GoLand/gin_practice/chap1/hello.tmpl")
if err != nil {
fmt.Println("http server failed:%V", err)
return
}
// 渲染模板
err = t.Execute(w, "小王子!")
if err != nil {
fmt.Println("http server failed:%V", err)
return
}
}
func main() {
http.HandleFunc("/hello", sayHello)
err := http.ListenAndServe(":9000", nil)
if err != nil {
fmt.Println("http server failed:%V", err)
return
}
}
使用html/template進(jìn)行HTML模板渲染的一般步驟如下:
定義模板:
首先,你需要定義HTML模板。可以在代碼中直接定義模板字符串,也可以將模板保存在獨(dú)立的文件中。比如我們創(chuàng)建了模板:hello.tmpl;創(chuàng)建模板對(duì)象:
使用template.New()函數(shù)創(chuàng)建一個(gè)模板對(duì)象。你可以選擇為模板對(duì)象指定一個(gè)名稱,以便在渲染過(guò)程中引用它。解析模板:
使用模板對(duì)象的Parse()或ParseFiles()方法解析模板內(nèi)容。如果模板內(nèi)容保存在單獨(dú)的文件中,可以使用ParseFiles()方法解析文件內(nèi)容并關(guān)聯(lián)到模板對(duì)象。渲染模板:
創(chuàng)建一個(gè)用于存儲(chǔ)模板數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu),并將數(shù)據(jù)傳遞給模板對(duì)象的Execute()方法。該方法將渲染模板并將結(jié)果寫入指定的輸出位置(如os.Stdout或http.ResponseWriter)。
在瀏覽器中輸入:127.0.0.0:9000/hello就可以看到結(jié)果:hello, 小王子!
(二)傳入其它數(shù)據(jù)進(jìn)行渲染
上次渲染,我們用的是這一句:err = t.Execute(w, "小王子!"),可以看到我們傳入了一個(gè)字符串而已。這次我們打算傳點(diǎn)稍微不同的其它數(shù)據(jù)。繼續(xù)編寫模板test.tmpl:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<title>Hello</title>
</head>
<body>
<p>姓名: {{.Name}}</p>
<p>年齡: {{.Age}}</p>
<p>性別: {{.Gander}}</p>
</body>
</html>
可以看到,我們的模板稍微復(fù)雜起來(lái)了!繼續(xù)編寫 main.go:
package main
import (
"fmt"
"html/template"
"net/http"
)
type User struct {
Name string
Gander string // 首字母是否大小寫,作為是否對(duì)外暴露的標(biāo)識(shí)
Age int
}
func sayHello(w http.ResponseWriter, r *http.Request) {
// 定義模板
u1 := User{
Name: "小王子",
Gander: "男",
Age: 19,
}
// 解析模板
t, err := template.ParseFiles("/Users/luliang/GoLand/gin_practice/chap2/test.tmpl")
if err != nil {
fmt.Println("ParseFiles failed:%V", err)
return
}
err = t.Execute(w, u1)
if err != nil {
return
}
}
func main() {
http.HandleFunc("/hello", sayHello)
err := http.ListenAndServe(":9000", nil)
if err != nil {
fmt.Println("http server failed:%V", err)
return
}
}
可以看到我們?cè)诶锩娑x了一個(gè)結(jié)構(gòu)類型 User,傳入了一個(gè) User 對(duì)象 u1:err = t.Execute(w, u1)
type User struct {
Name string
Gander string // 首字母是否大小寫,作為是否對(duì)外暴露的標(biāo)識(shí)
Age int
}
趕緊看看運(yùn)行結(jié)果,可以看到結(jié)果符合預(yù)期:
姓名: 小王子
年齡: 19
性別: 男
(三)定義函數(shù)參與渲染
定義我們的模板:f.tmpl:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<title>hello</title>
</head>
<body>
{{ kua . }}
</body>
</html>
可以看到,我們的模板里面有一個(gè)函數(shù)名字叫做 kua,沒(méi)錯(cuò),這就是我們要的函數(shù)。
編寫main.go:
package main
import (
"fmt"
"html/template"
"net/http"
)
func f(w http.ResponseWriter, r *http.Request) {
// 定義模板
k := func(name string) (string, error) {
return name + "太棒了!", nil
}
t := template.New("f.tmpl")
t.Funcs(template.FuncMap{
"kua": k,
})
// 解析模板
_, err := t.ParseFiles("/Users/luliang/GoLand/gin_practice/chap3/f.tmpl")
if err != nil {
return
}
// 渲染模板
err = t.Execute(w, "小王子")
if err != nil {
return
}
}
func main() {
http.HandleFunc("/hello", f)
err := http.ListenAndServe(":9002", nil)
if err != nil {
fmt.Println("http server failed:%V", err)
return
}
}
可以看到,我用了一個(gè)關(guān)鍵語(yǔ)句將函數(shù)kua 關(guān)聯(lián)到了模板:
t.Funcs(template.FuncMap{
"kua": k,
})
點(diǎn)擊運(yùn)行,可以看到結(jié)果:小王子太棒了!
(四)c.HTML() 渲染
在Gin框架中,可以使用c.HTML()方法來(lái)進(jìn)行HTML模板渲染。該方法接收HTTP狀態(tài)碼、模板名稱和渲染所需的數(shù)據(jù)作為參數(shù)。
編寫模板index.tmpl:
<!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>posts/index</title>
</head>
<body>
{{.title}}
</body>
</html>
可以看到我們只需渲染傳入對(duì)象的 title 值,編寫 main.go:
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
r := gin.Default()
r.LoadHTMLFiles("/Users/luliang/GoLand/gin_practice/chap4/index.tmpl")
r.GET("/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "index.tmpl", gin.H{
"title": "你好,前端真是太有意思了!",
})
})
r.Run(":9091")
}
首先,創(chuàng)建一個(gè) gin.default()路由對(duì)象,然后給該對(duì)象載入已經(jīng)寫好的模板文件,之后就可以用 GET 函數(shù)進(jìn)行請(qǐng)求了。
先看看 GET 是什么東西:
// GET is a shortcut for router.Handle("GET", path, handlers).
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
return group.handle(http.MethodGet, relativePath, handlers)
}
它說(shuō),GET 是router.Handle("GET", path, handlers)的一個(gè)捷徑。再看看router.Handle("GET", path, handlers)是什么東西:
func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
absolutePath := group.calculateAbsolutePath(relativePath)
handlers = group.combineHandlers(handlers)
group.engine.addRoute(httpMethod, absolutePath, handlers)
return group.returnObj()
}
// Handle registers a new request handle and middleware with the given path and method.
// The last handler should be the real handler, the other ones should be middleware that can and should be shared among different routes.
// See the example code in GitHub.
//
// For GET, POST, PUT, PATCH and DELETE requests the respective shortcut
// functions can be used.
//
// This function is intended for bulk loading and to allow the usage of less
// frequently used, non-standardized or custom methods (e.g. for internal
// communication with a proxy).
原來(lái)就是做了一下中間處理,注冊(cè)了一個(gè)新的請(qǐng)求句柄。它還說(shuō)GET, POST, PUT, PATCH、DELETE 都有類似的捷徑。之后在 handlers中放一個(gè)匿名函數(shù):
func(c *gin.Context) {
c.HTML(http.StatusOK, "index.tmpl", gin.H{
"title": "你好,前端真是太有意思了!",
})
}
這個(gè)函數(shù)就是用于渲染的函數(shù)。猜測(cè)c.HTML()依然是一個(gè) shortcut:
// HTML renders the HTTP template specified by its file name.
// It also updates the HTTP code and sets the Content-Type as "text/html".
// See http://golang.org/doc/articles/wiki/
func (c *Context) HTML(code int, name string, obj any) {
instance := c.engine.HTMLRender.Instance(name, obj)
c.Render(code, instance)
}
可以看到真正干活的還是 Render()。
點(diǎn)擊運(yùn)行,結(jié)果為:你好,前端真是太有意思了!
(五)從多個(gè)模板中選擇一個(gè)進(jìn)行渲染
如果要渲染多個(gè)文件,該如何操作?比如我們通過(guò)輸入不同的網(wǎng)站,服務(wù)器這時(shí)只需要選擇不同的模板進(jìn)行渲染就好。在這里創(chuàng)建了兩個(gè)模板文件,users/index.tmpl:
{{define "users/index.tmpl"}}
<!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>users/index</title>
</head>
<body>
{{.title}}
</body>
</html>
{{end}}
posts/index.tmpl:
{{define "posts/index.tmpl"}}
<!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>posts/index</title>
</head>
<body>
{{.title}}
</body>
</html>
{{end}}
這里使用了{{define "posts/index.tmpl"}}...{{end}}結(jié)構(gòu),對(duì)模板文件進(jìn)行了命名。
編寫main.go:
package main
import (
"github.com/gin-gonic/gin"
"html/template"
"net/http"
)
func main() {
r := gin.Default()
r.LoadHTMLGlob("/Users/luliang/GoLand/gin_practice/chap5/templates/**/*")
r.GET("/posts/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{
"title": "posts/index.tmpl",
})
})
r.GET("/users/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "users/index.tmpl", gin.H{
"title": "送你到百度!",
})
})
r.Run(":9091")
}
我們?cè)谠跒g覽器地址欄輸入http://127.0.0.1:9091/users/index可以得到:送你到百度!;而輸入http://127.0.0.1:9091/posts/index可以得到:posts/index.tmpl。
(六)加點(diǎn)東西,使得事情朝著有意思的方向進(jìn)行!
我們?yōu)榱耸沟镁W(wǎng)頁(yè)畫面更有趣,這里使用了多個(gè)文件,分別是index.css、index.js。編寫:index.css:
body {
background-color: #00a7d0;
}
沒(méi)錯(cuò)只有一句話。接下來(lái)編寫 index.js:
alert("Hello, Web!")
沒(méi)錯(cuò),也只有一句話。然后,我們把 css、js 文件引入到 index.tmpl中:
{{define "users/index.tmpl"}}
<!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">
<link rel="stylesheet" href="/xxx/index.css" rel="external nofollow" >
<title>users/index</title>
</head>
<body>
{{.title}}
<script src="/xxx/index.js"></script>
</body>
</html>
{{end}}
接下來(lái)寫main.go:
package main
import (
"github.com/gin-gonic/gin"
"html/template"
"net/http"
)
func main() {
r := gin.Default()
// 加載靜態(tài)文件
r.Static("/xxx", "/Users/luliang/GoLand/gin_practice/chap5/statics")
r.LoadHTMLGlob("/Users/luliang/GoLand/gin_practice/chap5/templates/**/*")
r.GET("/posts/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{
"title": "posts/index.tmpl",
})
})
r.GET("/users/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "users/index.tmpl", gin.H{
"title": "送你到百度!",
})
})
r.Run(":9091")
}
因?yàn)槲覀円?css、js 文件加載進(jìn)去,因此要用 r.Static()函數(shù)把放置靜態(tài)文件的目錄加入進(jìn)去。為了復(fù)用,第一個(gè)參數(shù)“/xxx”的意思是,只要index.tmpl文件中存在“/xxx”字段就直接指到我們?cè)O(shè)置的目錄。
運(yùn)行結(jié)果:首先 js 文件會(huì)首先渲染:

之后,css 文件會(huì)渲染:

可以看到這里出現(xiàn)了一個(gè)超鏈接,那是因?yàn)槲覀冊(cè)谶@個(gè)字符后面插入了一個(gè)函數(shù):
// 添加自定義函數(shù)
r.SetFuncMap(template.FuncMap{
"safe": func(str string) template.HTML {
return template.HTML(str)
},
})
配合 index.tmpl,就可以了。

二、利用已有模板進(jìn)行部署
通過(guò)以上的例子,終于學(xué)會(huì)了在模板中插入大量的css、js 進(jìn)行渲染了!
首先找到某個(gè)網(wǎng)站,比如站長(zhǎng)之家,下載一個(gè)模板:

選擇第一個(gè),下載之后解壓:

static 外面的 index.html有用,其它都可以忽略。把 static 里面的文件夾復(fù)制到我們的工作目錄:

同時(shí),把 index.html文件復(fù)制到 posts(無(wú)所謂,強(qiáng)迫癥而已),就可以使用了。
r.GET("/home", func(c *gin.Context) {
c.HTML(http.StatusOK, "index.html", nil)
})
這樣,在地址欄輸入:http://127.0.0.1:9091/home,就可以看到效果了:

渲染的相當(dāng)完美!這里會(huì)存在一些小細(xì)節(jié),比如在 index.html文件中需要把 css、js文件的地址進(jìn)行變更,變更到你放置這些文件的地址:

代碼附上main.go:
package main
import (
"github.com/gin-gonic/gin"
"html/template"
"net/http"
)
func main() {
r := gin.Default()
// 加載靜態(tài)文件
r.Static("/xxx", "/Users/luliang/GoLand/gin_practice/chap5/statics")
// 添加自定義函數(shù)
r.SetFuncMap(template.FuncMap{
"safe": func(str string) template.HTML {
return template.HTML(str)
},
})
r.LoadHTMLGlob("/Users/luliang/GoLand/gin_practice/chap5/templates/**/*")
r.GET("/posts/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{
"title": "posts/index.tmpl",
})
})
r.GET("/users/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "users/index.tmpl", gin.H{
"title": "<a ,
})
})
r.GET("/home", func(c *gin.Context) {
c.HTML(http.StatusOK, "index.html", nil)
})
r.Run(":9091")
}
這意味著,我們以后要想寫網(wǎng)頁(yè),根本不需要進(jìn)行大量的無(wú)意義的編程,利用 ChatGPT,我們可以寫出大量的優(yōu)秀的css、js 網(wǎng)頁(yè),我們要做的只是進(jìn)行適量的改動(dòng),這將極大地豐富我們的創(chuàng)造力!
三、總結(jié)
本文從簡(jiǎn)單到難,對(duì)Go Web中 gin 框架下的模板渲染進(jìn)行了簡(jiǎn)單的闡述。
到此這篇關(guān)于Go Web下gin框架的模板渲染的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Go gin 模板渲染內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
golang 實(shí)現(xiàn)tcp server端和client端,并計(jì)算RTT時(shí)間操作
這篇文章主要介紹了golang 實(shí)現(xiàn)tcp server端和client端,并計(jì)算RTT時(shí)間操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12
golang小游戲開(kāi)發(fā)實(shí)戰(zhàn)之飛翔的小鳥(niǎo)
這篇文章主要給大家介紹了關(guān)于golang小游戲開(kāi)發(fā)實(shí)戰(zhàn)之飛翔的小鳥(niǎo)的相關(guān)資料,,本文可以帶你你從零開(kāi)始,一步一步的開(kāi)發(fā)出這款小游戲,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-03-03
使用Go進(jìn)行單元測(cè)試的實(shí)現(xiàn)
這篇文章主要介紹了使用Go進(jìn)行單元測(cè)試的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11
golang定時(shí)器Timer的用法和實(shí)現(xiàn)原理解析
這篇文章主要介紹了golang定時(shí)器Ticker,本文主要來(lái)看一下Timer的用法和實(shí)現(xiàn)原理,需要的朋友可以參考以下內(nèi)容2023-04-04
Go語(yǔ)言實(shí)現(xiàn)lru淘汰策略和超時(shí)過(guò)期
緩存的大小是有限的,當(dāng)添加數(shù)據(jù)發(fā)現(xiàn)剩余緩存不夠時(shí),需要淘汰緩存中的部分?jǐn)?shù)據(jù),本文主要介紹了Go語(yǔ)言實(shí)現(xiàn)lru淘汰策略和超時(shí)過(guò)期,感興趣的可以了解一下2024-02-02
GoFrame框架數(shù)據(jù)校驗(yàn)之校驗(yàn)對(duì)象校驗(yàn)結(jié)構(gòu)體
這篇文章主要為大家介紹了GoFrame框架數(shù)據(jù)校驗(yàn)之校驗(yàn)對(duì)象校驗(yàn)結(jié)構(gòu)體示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06

