GO - GIN框架学习
一、模板补充
(1)修改默认的标识符
package main
import(
"fmt"
"net/http"
"html/template"
)
func index(w http.ResponseWriter,r *http.Request){
//定义模板
//解析模板
t,err := template.New("index.tmpl").
Delims("{[","]}").
ParseFiles("./web08/index.tmpl")
if err != nil{
fmt.Printf("parse template failed,err:%v\n",err)
}
//渲染模板
name := "寒假第一战"
err = t.Execute(w,name)
if err != nil{
fmt.Printf("execute template failed,err:%v\n",err)
return
}
}
func main(){
http.HandleFunc("/index", index)
err := http.ListenAndServe(":9000", nil)
if err != nil {
fmt.Printf("http server failed, err:%v\n", err)
return
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<title>修改模板引擎的标识符</title>
</head>
<body>
<div>Hello {[ . ]}</div>
</body>
</html>
放在一个文件夹下运行两个文件,可以得到单独一行的输出
Hello 寒假第一战
这里是在修改默认的标识符
Go标准库的模板引擎使用的花括号
{{和}}作为标识,而许多前端框架(如Vue和AngularJS)也使用{{和}}作为标识符,所以当我们同时使用Go语言模板引擎和以上前端框架时就会出现冲突,这个时候我们需要修改标识符,修改前端的或者修改Go语言的。这里演示如何修改Go语言模板引擎默认的标识符:template.New("test").Delims("{[", "]}").ParseFiles("./t.tmpl")与原来比较多了New和Delims
对应的我代码里其实是把{{}}变成了{[]},以另一种形式解析模板
Delims是一个函数
(2)text/template与html/tempalte的区别
html/template针对的是需要返回HTML内容的场景,在模板渲染过程中会对一些有风险的内容进行转义,以此来防范跨站脚本攻击。
其中常用就是<script>alert(123)</script>
这个时候传入一段JS代码并使用html/template去渲染该文件,会在页面上显示出转义后的JS内容。 <script>alert('嘿嘿嘿')</script> 这就是html/template为我们做的事。防止被无限循环等破坏服务器。
如果要相信用户输入的内容,不想被一起转义掉,就自己编写safe函数,手动返回一个template.HTML类型的内容。
package main
import (
"fmt"
"html/template"
"net/http"
)
func xss(w http.ResponseWriter, r *http.Request) {
//定义模板
//解析模板
//解析模板之前定义一个自定义的函数safe再解析
t, err := template.New("xss.tmpl").Funcs(template.FuncMap{
"safe": func(str string) template.HTML {
return template.HTML(str)
},
}).ParseFiles("./web08/xss.tmpl")
if err != nil {
fmt.Printf("parse template failed,err:%v\n", err)
return
}
//渲染模板
str1 := "<script>alert(123);</script>"
str2 := "<a href='http://hdufhq.cn:8888/'>hdu孵化器地址</a>"
t.Execute(w, map[string]string{
"str1": str1,
"str2": str2,
})
}
func main() {
http.HandleFunc("/xss", xss)
err := http.ListenAndServe(":9000", nil)
if err != nil {
fmt.Printf("http server failed, err:%v\n", err)
return
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>xss</title>
</head>
<body>
用户的评论是:{{.str1}}
用户的评论是:{{.str2 | safe}}
</body>
</html>
这里的safe操作{{. | safe}}就是让用户输入不被转义进行调用自己编写的函数的写法。好像也可以{{safe .}}这么写
解析模板的地方先新建了xss.tmpl的模板,然后Funcs调用函数,然后定义一个函数映射集合,里面定义了safe自定义函数,用来返回我们str内容变成HTML的string,在最后才解析模板。
渲染的时候使用了映射,字符串对应字符串,可以多个数据传输过去。
这个第二个part主要讲的是go的包template自带防止被脚本攻击的转义功能,如果想要不转义,就自己编写函数并在tmpl文件里调用它。
最后的结果就可以变成如下:
前面是转义结果,后面是避免了转移,自带了链接(夹带私货证明自己学的bushi)。
二、gin框架模板渲染
(1)首个实例
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main(){
//创建一个默认路由
r := gin.Default()
//解析模板
r.LoadHTMLFiles("./web09/templates/index.tmpl")
//渲染模板
r.GET("/index",func(c *gin.Context){
//HTTP请求要有状态码的响应被返回 三次握手 渲染上数据
c.HTML(http.StatusOK,"index.tmpl",gin.H{
"title" : "http://hdufhq.cn:8888/",
})
})
//启动server 这里浏览器就是客户端 本机是服务器
r.Run(":9090")
}
所有笔记都在注释里了,一步步走。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>posts/index</title>
</head>
<body>
{{.title}}
</body>
</html>
搭配的模板里只需要在点号传入title数据就可以了。
Gin框架中使用LoadHTMLGlob()或者LoadHTMLFiles()方法进行HTML模板渲染。
(2)多个模板一起渲染的情况
首先要先在templates文件夹下新建posts和users文件夹,都各自有一个index.tmpl文件
所以要区分这两个相同的文件,可以在开头加入语句{{define "users/index.tmpl"}}(若是posts文件夹就换一下),在结尾加上{{end}}
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main(){
//创建一个默认路由
r := gin.Default()
//解析模板
//旧方法解析r.LoadHTMLFiles("./web09/templates/index.tmpl")
r.LoadHTMLGlob("web09/***/**/*") //意思是直接加载这个web09下面的所有文件 不用具体指定路径了
//渲染模板 在这里写上上面星号的相对路径
r.GET("posts/index",func(c *gin.Context){
//HTTP请求要有状态码的响应被返回 三次握手 渲染上数据
c.HTML(http.StatusOK,"index.tmpl",gin.H{
"title" : "http://hdufhq.cn:8888/",
})//不可行的写法,无法展示title内容,应该是找不到文件
})
r.GET("users/index",func(c *gin.Context){
//HTTP请求要有状态码的响应被返回 三次握手 渲染上数据
c.HTML(http.StatusOK,"users/index.tmpl",gin.H{
"title" : "https://www.hdu.edu.cn/main.htm",
})//可行写法
})
//启动server 这里浏览器就是客户端 本机是服务器
r.Run(":9090")
}
这里出现了一个很好玩的情况:
视频里是只解析到二级目录,因为仓库关系我没有新建文件夹,所以在原来文件夹里写,多了一层文件壳,我就尝试了一下用三个*来解析web09下的所有文件,结果是可行的!!虽然本来gpt跟我说最多到两级。
有个问题是,我调试的时候发现post这种HTML的name写法是读不出title的,虽然还能GET但是没有内容。但是下面那种写法是可以正常展现title的,我觉得可能是为了区分相同文件名也就是index这个文件需要至少外写一层文件夹。(后来发现可能是因为文件首行里手写define了
GET那个路径纯粹是路由相对路径,下面c.HTML函数才是需要找到自己文件的。
改进写法
r.GET("users/index", func(c *gin.Context) { //HTTP请求要有状态码的响应被返回 三次握手 渲染上数据 c.HTML(http.StatusOK, "users/index.tmpl", gin.H{ "title": "<a href='https://www.hdu.edu.cn/main.htm'>杭电地址</a>", }) })
这个写法的显示结果是单纯文字,显然是被当成风险转义了,不想转义,让它变成可点击链接就得继续改写。
改写过程遇到一个问题就是引包。
要区分这两个,否则template.HTML会显示是undefined。
html/template和text/template是Go语言中用于处理模板的两个包,它们之间的区别在于对待输出的方式。
text/template:这个包主要用于处理纯文本模板,不会对输出进行特殊处理。所有的变量在输出时都会被转义,以确保安全性。这意味着如果您在模板中使用类似{{ .Content }}的表达式,其中.Content包含HTML代码,那么输出将会把HTML标签转义为实体字符,而不会被当作HTML代码渲染。html/template:相比于text/template,html/template提供了更多的HTML相关功能。它会自动对输出进行HTML转义,以避免XSS(跨站脚本攻击)等安全问题。在html/template中,通过使用template.HTML类型来指示某个输出是安全的HTML内容,不需要进行转义。您可以使用template.FuncMap注册自定义函数,并在函数中返回template.HTML类型的值。
在避免转移时候用的是第二个加上自定义函数,前面编写用的是第一个。
package main
import (
"net/http"
"html/template"
"github.com/gin-gonic/gin"
)
func main() {
//创建一个默认路由
r := gin.Default()
//解析模板
//先给gin框架中模板添加自定义函数
r.SetFuncMap(template.FuncMap{
"safe" : func(str string) template.HTML{
return template.HTML(str)
},
})
//旧方法解析r.LoadHTMLFiles("./web09/templates/index.tmpl")
r.LoadHTMLGlob("web09/***/**/*") //意思是直接加载这个web09下面的所有文件 不用具体指定路径了
//渲染模板 在这里写上上面星号的相对路径
r.GET("posts/index", func(c *gin.Context) {
//HTTP请求要有状态码的响应被返回 三次握手 渲染上数据
c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{
"title": "<a href='http://hdufhq.cn:8888/'>孵化器地址</a>",
})
})
r.GET("users/index", func(c *gin.Context) {
//HTTP请求要有状态码的响应被返回 三次握手 渲染上数据
c.HTML(http.StatusOK, "users/index.tmpl", gin.H{
"title": "<a href='https://www.hdu.edu.cn/main.htm'>杭电地址</a>",
})
})
//启动server 这里浏览器就是客户端 本机是服务器
r.Run(":9090")
}
na
那边文件里写成{{.title | safe}}可以展示为两个链接
(3)静态文件处理
静态文件:html页面上用到的样式文件 如css js文件 图片
当我们渲染的HTML文件中引用了静态文件时,我们只需要按照以下方式在渲染页面前调用gin.Static方法即可。
在解析之前插入
//加载静态文件 意思是直接去右边这个目录找文件
//然后在浏览器f12打开的代码是左边这个链接,右边是被指代隐藏了吧
r.Static("/xxx","./web09/statics")
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/xxx/index.css">
<title>users/index</title>
</head>
用link来插入连接的样式表
我感觉/xxx这个就是在指代index的长长的文件夹路径,方便前端链接书写。
r是一个 Gin 的实例,用于处理 HTTP 请求和路由。
.Static()是 Gin 框架提供的方法之一,用于配置静态文件服务。
"/xxx"是在服务器上暴露的 URL 路径,即访问静态文件时需要使用的路径。例如,如果设置为/xxx,那么访问静态文件时的 URL 就是http://yourdomain/xxx/filename.ext。
"./web09/statics"是本地文件系统上的静态文件目录路径。在这个例子中,静态文件存储在项目根目录下的 "web09/statics" 文件夹中。通过以上代码,当用户在浏览器中请求 "/xxx/filename.ext" 时,Gin 框架会自动去 "./web09/statics/filename.ext" 路径下寻找对应的静态文件,并将其返回给客户端。
请注意,
/xxx可以自定义为其他路径,而./web09/statics则应该根据实际情况修改为您的静态文件目录路径。
.
其中:终端
[GIN] 2024/02/15 - 22:55:28 | 200 | 1.0184ms | 127.0.0.1 | GET "/users/index" [GIN] 2024/02/15 - 22:55:28 | 200 | 74.0016ms | 127.0.0.1 | GET "/xxx/index.css"
浏览器访问的时候发送了两次请求,在渲染的时候额外需要一个css,浏览器会又发一次请求获得css。
若添加js文件
alert(123);
<body>
{{.title | safe}}
<script src="/xxx/index.js"></script>
</body>
会在页面上先弹窗出123的响应,可见上传附带图片4。
[GIN] 2024/02/16 - 21:02:37 | 200 | 2.2963ms | 127.0.0.1 | GET "/users/index" [GIN] 2024/02/16 - 21:02:38 | 200 | 77.6227ms | 127.0.0.1 | GET "/xxx/index.js" [GIN] 2024/02/16 - 21:02:38 | 200 | 93.6283ms | 127.0.0.1 | GET "/xxx/index.css"
(4)使用模板继承
Gin框架默认都是使用单模板,如果需要使用
block template功能,可以通过"github.com/gin-contrib/multitemplate"库实现,具体示例如下:首先,假设我们项目目录下的templates文件夹下有以下模板文件,其中
home.tmpl和index.tmpl继承了base.tmpl:templates ├── includes │ ├── home.tmpl │ └── index.tmpl ├── layouts │ └── base.tmpl └── scripts.tmpl
犯了通配符的路径的错误,搞了半天搜不到解决方法,找了哥,最后可能还是glob函数使用不能三个*的原因。
package main
import (
"net/http"
"html/template"
"github.com/gin-gonic/gin"
)
func main() {
gin.SetMode(gin.ReleaseMode)
//创建一个默认路由
r := gin.Default()
//解析模板
//加载静态文件
r.Static("statics","/statics")
//先给gin框架中模板添加自定义函数
r.SetFuncMap(template.FuncMap{
"safe" : func(str string) template.HTML{
return template.HTML(str)
},
})
//旧方法解析r.LoadHTMLFiles("./web09/templates/index.tmpl")
r.LoadHTMLGlob("templates/**/*") //意思是直接加载这个web09下面的所有文件 不用具体指定路径了
//渲染模板 在这里写上浏览器上url后小截
r.GET("/posts/index", func(c *gin.Context) {
//HTTP请求要有状态码的响应被返回 三次握手 渲染上数据
c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{
"title": "<a href='http://hdufhq.cn:8888/'>孵化器地址</a>",
})
})
r.GET("/users/index", func(c *gin.Context) {
//HTTP请求要有状态码的响应被返回 三次握手 渲染上数据
c.HTML(http.StatusOK, "users/index.tmpl", gin.H{
"title": "<a href='https://www.hdu.edu.cn/main.htm'>杭电地址</a>",
})
})
//返回从网上下载的模板,没有需要上传的数据,所以是nil
r.GET("/home",func(c *gin.Context){
c.HTML(http.StatusOK,"posts/home.html",nil)
})
//启动server 这里浏览器就是客户端 本机是服务器
r.Run(":9090")
}
把这个整个文件夹拿出来单独运行,保证最多就两级目录,这样就可以运行了,gin框架安装和初始没有问题。
这样就可以实现前端模板的一个全栈应用了。