在實務上, 也不會只有做WebApi專案.
也會有做WebServe的專案, 差別在那 ??
最顯著的差別就是有沒有View.
Gin有提供載入View並且把參數給填入template中再渲染的功能.
來玩看看.
建立一個view資料夾, 在加入一個index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Gin Hello</title>
</head>
<body>
<h1>Hi IT Home</h1>
</body>
</html>
好...但要告訴Gin, 靜態網頁在哪裡吧?
不然它不會聰明到知道頁面在這資料夾內.
這隻API就是載入指定的文件夾下所有的靜態頁面.
之後我們在透過Context.HTML直接渲染網頁返回給瀏覽器.
// LoadHTMLGlob loads HTML files identified by glob pattern
// and associates the result with HTML renderer.
func (engine *Engine) LoadHTMLGlob(pattern string) {...}
回來修改SetupRouter.go, 使用這API來載入, 把之前的mux也給搬進來
func SetupRouter() *gin.Engine {
router := gin.Default()
router.LoadHTMLGlob("view/*")
...
}
再來新增一個handler處理首頁的請求indexHandler.go
package handler
import (
"net/http"
"github.com/gin-gonic/gin"
)
func GetIndex(ctx *gin.Context) {
ctx.HTML(http.StatusOK, "index.html", nil)
}
一樣註冊進去routing table
indexRouting := router.Group("/")
{
indexRouting.GET("", handler.GetIndex)
}
執行看看
go run main.go
棒!! 記得補上測試XD
很多時候的頁面內容都不可能是靜態資訊...
是會隨時隨地因人而看到的內容略有不同.
Gin支援tmpl的渲染跟資料綁定.
來玩看看
把index.html改成index.tmpl
內容改成使用{{ .屬性名稱 }}, 這樣的方式表示要由gin來動態動入的內容.
<h1>Hi {{.title}}</h1>
修改indexHandler.go
傳入一組map[string]interface{}
string要是tmpl的屬性名稱.
func GetIndex(ctx *gin.Context) {
// ctx.HTML(http.StatusOK, "index.html", nil)
ctx.HTML(http.StatusOK, "index.tmpl", gin.H{
"title": "IT Home again",
})
}
重新執行, 刷新頁面
來寫index的測試
package test
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"github.com/tedmax100/gin-angular/router"
)
var engine *gin.Engine
func init() {
gin.SetMode(gin.TestMode)
engine = router.SetupRouter()
}
func TestIndexGetRouter(t *testing.T) {
w := httptest.NewRecorder()
req, _ := http.NewRequest(http.MethodGet, "/", nil)
engine.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
}
// panic: html/template: pattern matches no files: `view/*`
疑? 找不到符合這路徑的html/template檔案.
原因在於該路徑是相對路徑, 是相對於現在執行的入口檔案所在的位置.
我們平常執行的是main.go, 該同層目錄下就有view/這資料夾.
單元測試跑得是/test/xxx_test.go
它們同層下可沒這目錄可載入.
所以解法有
在我們每次執行go run main.go
時, 都會出現這段訊息.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
Gin定義了3種Mode
const (
// DebugMode indicates gin mode is debug.
DebugMode = "debug"
// ReleaseMode indicates gin mode is release.
ReleaseMode = "release"
// TestMode indicates gin mode is test.
TestMode = "test"
)
清楚的告訴我們能用env或是SetMode去定義.
把SetupRouter.go()改成這樣
判斷mode, 去載入相對於自己不同位置的view
test的上一層才能看得到veiw所以要先往上爬一層,再去載入.
func SetupRouter() *gin.Engine {
router := gin.Default()
if mode := gin.Mode(); mode == gin.TestMode {
router.LoadHTMLGlob("./../view/*")
} else {
router.LoadHTMLGlob("view/*")
}
...
}
go run test ./...
繼續, 有了tmpl了.
那js、css、image這類的靜態資源總要吧?
先來下載Bootstrap
Bootstrap內剛好有js+css, 來美化我們的網頁
在專案根目錄開個asset資料夾, 內有js、css、img這三個資料夾.
把bootstrap解壓縮到對應的資料夾內.
老樣子要告訴Gin這些asset在哪裡.
一樣修改SetupRouter(), 這裡新增呼叫Static()這隻API.
func SetupRouter() *gin.Engine {
router := gin.Default()
if mode := gin.Mode(); mode == gin.TestMode {
router.LoadHTMLGlob("./../view/*")
} else {
router.LoadHTMLGlob("view/*")
}
router.Static("/assetPath", "./asset")
...
}
// Static serves files from the given file system root.
// Internally a http.FileServer is used, therefore http.NotFound is used instead
// of the Router's NotFound handler.
// To use the operating system's file system implementation,
// use :
// router.Static("/static", "/var/www")
func (group *RouterGroup) Static(relativePath, root string) IRoutes {
return group.StaticFS(relativePath, Dir(root, false))
}
第一個參數是說我們對著gin註冊一個靜態資源目錄.
第二個參數才是這些靜態資源的真實位置
舉例: 也許最後這些資源都會被bundle放到dist/內.
那我們第二個參數就會改成dist/...
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="/assetPath/css/bootstrap.min.css">
<link rel="stylesheet" href="/assetPath/css/bootstrap-grid.min.css">
<link rel="stylesheet" href="/assetPath/css/bootstrap-reboot.min.css">
<script rel="script" src="/assetPath/js/bootstrap.bundle.js"></script>
<title>Gin Hello</title>
</head>
<body>
<h1>Hi {{.title}}</h1>
</body>
</html>
執行看看!
水, 正常.
因為index.tmpl 就只是個template.
隨時改存檔都會隨時變更, 不必重新編譯.