gin框架 HTML 模板加载,渲染 使用详解和总结

news/2024/7/7 16:24:18 标签: gin, golang, go, gin框架, html模板渲染, 模板加载

gin框架中默认的HTML模板渲染使用  LoadHTMLGlob() 或者 LoadHTMLFiles() , 这个地方如果是使用的LoadHTMLGlob() 这个方法的话是有坑的,即当你的模板文件放在不同的文件夹中时,使用这个方式加载会将文件夹也作为文件加载进去,从而直接在模板渲染的时候抛异常!! 

假设在我们的项目templates文件夹中有如下结构的模板文件:

├── article
│   ├── detail.html
│   └── index.html
├── footer.html
├── header.html
├── index.html
└── search
    ├── detail.html
    └── index.html

r := gin.Default()

LoadHTMLGlob模板加载匹配模式

 r.LoadHTMLGlob("templates/*.html")    如果是这种模式就只能加载顶层的3个html文件,子目录中的文件无法加载

 r.LoadHTMLGlob("templates/**/*.html")   这种方式就只能加载子目录article, search中一共4个文件

r.LoadHTMLGlob("template/**/*")  这种模式 只能加载子目录中的所有文件或者文件夹,顶层的3个模板无法加载

r.LoadHTMLGlob("template/**")  这种模式 可以加载所有的文件和目录(坑,模板编译时直接报异常, 不能使用这种方式!)

上面的4种方式加载模板对于我们上面的模板结构都不可用,直接抛弃! 

上面的方式只能适用于没有子目录的模板加载

filepath.WalkDir  + LoadHTMLFiles() 的方式加载模板

对于我们上面的模板结构只能采用LoadHTMLFiles()方式加载了, 使用这个方式加载,我们需要先自己加载我们需要的模板文件, 这个就需要使用filepath.WalkDir 这个目录遍历大杀器出场了。废话补多少直接删代码:

// 模板文件加载
	var tplFiles []string
	filepath.WalkDir("templates", func(path string, d fs.DirEntry, err error) error {
		if !d.IsDir() && strings.HasSuffix(d.Name(), ".html") {
			// 非目录,且是.html结尾的文件加入到模板文件列表
			tplFiles = append(tplFiles, path)
		}
		return nil
	})

	r.LoadHTMLFiles(tplFiles...) // 加载html模板

 在gin控制器中通过.HTML方法使用模板

这个非常简单直接用HTML方法即可。 需要注意的是这里的模板名称是很讲究的,我们来看一下这个HTML方法的原型: func (c *gin.Context) HTML(code int, name string, obj any)   

这里第一个参数是http的状态码一般都是200即可, 第二个参数name 就是我们要渲染的模板的名称,这里的名称默认是你加载的文件的不区分目录的文件名, 由于默认不区分目录,所以如果你有多个同名的文件,则必须在对应的文件中使用 define对文件名进行定义,如: {{ define "index.html"}}   你的模板内容  {{end}}   。  如果你加载的文件名每个都是唯一的,则不需要定义也可以直接使用。


r.GET("/index", func(c *gin.Context) {
    // 注意这里的第二个参数 模板名是很有讲究的
    c.HTML(http.StatusOK, "index.html", gin.H{
        "title": "Main website",
    })
})
r.Run(":8080")

html模板文件示例和模板包含命名定义使用示例

index.html 示例:

注意,由于index.html文件名称有重复,所以这里我们必须使用define对文件名进行定义,否则index.html加载的就可能不是你期望的文件!!!

另外这里我们使用内置函数 template 对另外2个公共的模板文件进行加载。 {{template "xxx.html" .}}

{{define "index.html"}}
<!DOCTYPE html>
<html>
  <head>
    {{template "header.html" .}}
  </head>

  <body>
    <div class="container-fluid">
      <H1>Hello Gin HTML</H1>
      <!--- footer --->
      {{template "footer.html" .}}
    </div>
  </body>
</html>
{{end}}

head.html

这个head.html文件名是唯一的,所以不需要是哟共define定义也可使用, 注意这里的标题 {{.title}} 这个直接使用了 c.HTML方法的第三个参数中传递的动态变量

<meta charset="utf-8" />
<meta
  name="viewport"
  content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<title>{{.title}}</title>

footer.html

<!--- footer --->
<footer style="height: 100px; background: #1b1c1d">
  <div style="text-align: center; font-size: 14px; padding-top: 50px">
    By
    <a href="http://dev.tekin.cn" target="_blank"
      >{{.title}}</a>
  </div>
</footer>

article/index.html文件内容

注意这里的define定义的文件名 {{ define "article/index.html"}}   我们一般以当前文件所在的模板文件夹下的相对路径来命名。 在c.HTML里面使用的时候我们就使用 c.HTML(200, "article/index.html", nil) 即可。

{{ define "article/index.html"}}
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Article index</title>
    <link rel="stylesheet" href="">
</head>
<body>
    <h1>Hello my article</h1>
</body>
</html>

{{end}}

自定义模板渲染器

你可以使用自定义的 html 模板渲染

import "html/template"

func main() {
	router := gin.Default()
	html := template.Must(template.ParseFiles("file1", "file2"))
	router.SetHTMLTemplate(html)
	router.Run(":8080")
}

自定义分隔符

r.Delims("{[{", "}]}") // 自定义模板分隔符 这个一般情况下我们保持默认即可

自定义模板功能

这个就是使用.SetFuncMap加载我们自定义的函数进去就可以。 推荐在c.HTML里面绑定对象的方式调用对象方法更方便,调用方式为 将函数名换为   对象名.方法名

import (
    "fmt"
    "html/template"
    "net/http"
    "time"

    "github.com/gin-gonic/gin"
)

func formatAsDate(t time.Time) string {
    year, month, day := t.Date()
    return fmt.Sprintf("%d/%02d/%02d", year, month, day)
}

func main() {
    router := gin.Default()
    router.Delims("{[{", "}]}")
    router.SetFuncMap(template.FuncMap{
        "formatAsDate": formatAsDate,
    })
    router.LoadHTMLFiles("templates/index.html")

    router.GET("/raw", func(c *gin.Context) {
        c.HTML(http.StatusOK, "raw.tmpl", map[string]interface{}{
            "now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC),
        })
    })

    router.Run(":8080")
}

在模板中使用我们自定义的函数:

Date: {[{.now | formatAsDate}]}   这个是使用管道符 | 方式,

也可以这样使用  Date: {[{formatAsDate  .now }]}  如果是多个参数用空格分开即可。

 附录 go内置的默认模板函数

func builtins() FuncMap {
	return FuncMap{
		"and":      and,
		"call":     call,
		"html":     HTMLEscaper,
		"index":    index,
		"slice":    slice,
		"js":       JSEscaper,
		"len":      length,
		"not":      not,
		"or":       or,
		"print":    fmt.Sprint,
		"printf":   fmt.Sprintf,
		"println":  fmt.Sprintln,
		"urlquery": URLQueryEscaper,

		// Comparisons
		"eq": eq, // ==
		"ge": ge, // >=
		"gt": gt, // >
		"le": le, // <=
		"lt": lt, // <
		"ne": ne, // !=
	}
}

gin框架HTML渲染使用总结:

1. gin框架中渲染模板必须要先加载,如果模板文件中没有子目录可以使用 LoadHTMLGlob() ,如果有子目录推荐使用 filepath.WalkDir  + LoadHTMLFiles() 的方式加载模板;

2. 模板名称默认为加载的文件的忽略目录的文件名, 如果有多个同名文件,则默认使用功能的是第一个加载的模板文件;

3. 在模板中包含其他模板可以使用template函数进行加载包含,即 {{template "xxx.html" .}}

4. gin中模板渲染使用c.HTML方法,这个方法有3个参数,第一个是http状态码,第二个是模板名称,第三个是要传递给模板的变量数据, 这个我们一般使用map数据结构

5. 自定义模板函数实际上就是将自己定义的函数使用 r.SetFuncMap(template.FuncMap{ "模板中使用的函数名": 函数名,})

6. 一般情况下我们可以通过在c.HTML的第三个参数里面绑定一个对象来代替自定义函数, 直接在模板中调用对象的方法更加方便和简洁,不需要另外绑定函数即可使用。


http://www.niftyadmin.cn/n/5534628.html

相关文章

实战篇:GY-906红外测温模块 + 万年历(定时器计数中断版本) -STM32篇

本文章基于兆易创新GD32 MCU所提供的2.2.4版本库函数开发 向上代码兼容GD32F450ZGT6中使用 后续项目主要在下面该专栏中发布&#xff1a; https://blog.csdn.net/qq_62316532/category_12608431.html?spm1001.2014.3001.5482 感兴趣的点个关注收藏一下吧! 电机驱动开发可以跳转…

农业四情监测设备——提高农业生产的效率和质量

TH-Q1农业四情监测设备是用于实时监测农业领域的四大关键监测内容的设备&#xff0c;这些内容包括土壤墒情、苗情、病虫情和灾情。以下是关于农业四情监测设备的详细介绍&#xff1a; 主要用于实时测量农田土壤的水分状况。包含土壤湿度传感器、土壤温度传感器等&#xff0c;安…

32 - 判断三角形(高频 SQL 50 题基础版)

32 - 判断三角形 select *,if(xy>z and xz>y and zy > x,Yes,No) triangle fromTriangle;

[保姆级教程]uniapp小程序获取右上角胶囊位置信息

文章目录 导文使用uni.getMenuButtonBoundingClientRect();方法实现完整案例 隐藏默认导航栏&#xff1a;全局隐藏当前页面隐藏 导文 uniapp小程序获取右上角胶囊位置信息 使用uni.getMenuButtonBoundingClientRect();方法实现 <script>const menuButtonInfo uni.getMe…

IOS Swift 从入门到精通:闭包 第一部分

文章目录 创建基本闭包在闭包中接受参数从闭包返回值闭包作为参数尾随闭包语法创建基本闭包 Swift 允许我们像使用字符串和整数等其他类型一样使用函数。这意味着您可以创建一个函数并将其分配给一个变量,使用该变量调用该函数,甚至可以将该函数作为参数传递给其他函数。 以…

C++ 结构体对齐详解

目录 前言 一、为什么要对结构体进行对齐操作&#xff1f; 二、基本概念 三、 对齐规则 四、示例讲解 1.简单的变量对齐 2.结构体包含有结构体的对齐 结构体成员详细解析 五、使用指令改变对齐方式 __attribute__((packed)) #pragma pack(push, n) #pragma pack(pop) …

【STM32+FPGA】先进算力+强安全+边缘AI,64位STM32MP2聚焦工业4.0应用

工业应用数字化和智能化程度&#xff0c;是衡量新质生产力的重要标准。STM32最新一代64位微处理器STM32MP2凭借先进算力、丰富接口和高安全性&#xff0c;为高性能和高度互联的工业4.0应用赋能。 STM32MP2四大关键特性&#xff0c;为工业4.0应用赋能 STM32MP2系列的第一颗产品S…

宁波银行票据案例解读,要注入科技赋能票据新形式

随着科技的飞速发展&#xff0c;金融行业正迎来一场前所未有的变革。作为一家以科技创新为驱动的现代化银行&#xff0c;宁波银行在这场变革中积极探索&#xff0c;宁波银行票据案例之后持续通过引入先进技术&#xff0c;为客户提供更加高效、智能的金融服务。 宁波银行推出的…