G0第21章 :gin框架介绍、RESTful API、Gin渲染

这篇具有很好参考价值的文章主要介绍了G0第21章 :gin框架介绍、RESTful API、Gin渲染。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

G0第21章 :gin框架

01 内容介绍

https://gin-gonic.com/zh-cn/docs/

G0第21章 :gin框架介绍、RESTful API、Gin渲染
G0第21章 :gin框架介绍、RESTful API、Gin渲染

web本质

  • Web是基于HTTP协议进行交互的应用网络
  • Web就是通过使用浏览器/APP访问的各种资源

G0第21章 :gin框架介绍、RESTful API、Gin渲染

G0第21章 :gin框架介绍、RESTful API、Gin渲染
G0第21章 :gin框架介绍、RESTful API、Gin渲染

package main

import (
   "fmt"
   "net/http"
)


func sayHello(w http.ResponseWriter, r *http.Request){
   _, _ = fmt.Fprintln(w, "<h1>Hello Golang!</h1>")
}


func main (){
   http.HandleFunc("/hello",sayHello)
   err := http.ListenAndServe(":9090",nil)
   if err != nil {
   	fmt.Println("http server failed, err=", err)
   	return
   }
}

G0第21章 :gin框架介绍、RESTful API、Gin渲染

gin框架初识

Gin是一个用Go语言编写的web框架。
它是一个类似于 martini但拥有更好性能的API框架,
由于使用了 httprouter,速度提高了近40倍。
如果你是性能和高效的追求者,你会爱上Gin

Gin框架介绍

Go世界里最流行的Web框架,Github。上有32K+star。基于httprouter开发的Web框架。中文文档齐全,简单易用的轻量级框架。

package main

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




func main(){
	r := gin.Default()//返回默认的路由引擎
	//指定用户使用GET请求访问/hello时,执行sayHello这个函数
	r.GET("/hello",func(c *gin.Context){c.JSON(200,gin.H{"message":"hello golang!",})})

	//启动服务
	r.Run(":9090")
}

以下内容为网页中需要显示的界面

<h1 style='color:orange'>Hello Golang!</h1>
<h2>how are you!</h2>
<img id='i1' src='https://img0.baidu.com/it/u=351616666,1871778470&fm=253&fmt=auto&app=138&f=PNG?w=727&h=500'>
<button id='b1'>点我</button>
<script>
document.getElementById('b1').οnclick=function(){
    document.getElementById('i1').src='https://img0.baidu.com/it/u=4064084738,3175031420&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=450'
}
</script>
当一个网页上的信息非常大的时候,那么通过http协议发送的消息内容也就非常的多,后端返回json格式的数据,可以在不同的平台应用,比如手机、app、网页

02 Gin框架安装与使用

安装

go get -u github.com/gin-gonic/gin

第一个Gin示例:

package main

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

func main() {
	// 创建一个默认的路由引擎
	r := gin.Default()
	// GET:请求方式;/hello:请求的路径
	// 当客户端以GET方法请求/hello路径时,会执行后面的匿名函数
	r.GET("/hello", func(c *gin.Context) {
		// c.JSON:返回JSON格式的数据
		c.JSON(200, gin.H{
			"message": "Hello world!",
		})
	})
	// 启动HTTP服务,默认在0.0.0.0:8080启动服务
	r.Run()
}

将上面的代码保存并编译执行,然后使用浏览器打开127.0.0.1:8080/hello就能看到一串JSON字符串。

03 RESTful API软件架构的风格

REST与技术无关,代表的是一种软件架构风格,REST是Representatiional State Transfer 的简称,中文翻译为“表征状态转移”或“表现层状态转化”。

G0第21章 :gin框架介绍、RESTful API、Gin渲染

简单来说,REST的含义就是客户端与Web服务器之间进行交互的时候,使用HTTP协议中的4个请求方法代表不同的动作。

  • GET用来获取资源
  • POST用来新建资源
  • PUT用来更新资源
  • DELETE用来删除资源

只要API程序遵循了REST风格,那就可以称其为RESTful API。目前在前后端分离的架构中,前后端基本都是通过RESTful API来进行交互的。
G0第21章 :gin框架介绍、RESTful API、Gin渲染
G0第21章 :gin框架介绍、RESTful API、Gin渲染
Gin框架支持开发RESTful API的开发。

package main

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




func main(){
	r := gin.Default()//返回默认的路由引擎


	//r.GET("/book", ...)
	//r.GET("/create_book", ...)
	//r.GET("/upadte_book", ...)
	//r.GET("/update_book", ...)

	//指定用户使用GET请求访问/hello时,执行sayHello这个函数
	r.GET("/hello",func(c *gin.Context){
			c.JSON(200,gin.H{
					"message":"hello golang!",
			})
	})
	r.POST("/hello",func(c *gin.Context)
			{c.JSON(200,gin.H{
					"message":"hello golang!",
			})
	})
	r.PUT("/hello",func(c *gin.Context)
			{c.JSON(200,gin.H{
					"message":"hello golang!",
			})
	})
	r.DELETE("/hello",func(c *gin.Context)
			{c.JSON(200,gin.H{
					"message":"hello golang!",
			})
	})


	//启动服务
	r.Run(":9090")
}

开发RESTful API的时候我们通常使用Postman来作为客户端的测试工具

04 Gin渲染

html/template 包实现了数据驱动的模版,用于生成可防止代码注入的安全的HTML内容。它提供了和text/template包相同的接口,Go语言中输出HTML的场景都应使用html/template这个包。

1、扫盲

模版与渲染

在一些前后端不分离的Web架构中,我们通常需要再后端将一些数据渲染到HTML文档中,从而实现动态的网页(网页的布局和样式大致一样,但展示的内容并不一样)效果。

我们这里说的模版可以理解为事先定义好的HTML文档文件,模板渲染的作用机制可以简单理解为文本替换操作-使用相应的数据去替换HTML文档中事先准备好的标记。

很多编程语言的Web框架中都使用各种模版引擎,比如Python语言中Flask框架中使用的jinja2模版引擎。

Go语言的模版引擎

Go语言内置了文本模版引擎 text/template 和用于HTML文档的html/template。它们的作用机制可以简单归纳如下:

1、模版文件通常定义为.tmpl和.tpl为后缀(也可以使用其他后缀),必须使用UTF8编码。
2、模版文件中使用{{和}}包裹和标识需要传入的数据。
3、传给模版这样的数据就可以通过点号(.)来访问,如果数据是复杂类型的数据,可以通过{{.FieldName}}来访问它的字段
4、除{{和}}包裹外的内容外,其他内容均不做修改原样输出。

模版引擎的使用

Go语言模版引擎的使用可以分为三部分:定义模版文件、解析末班文件、模版渲染

第一步:定义模版文件

其中,定义模版文件时需要我们按照相关语法规则去编写,后文会详细介绍。

第二步:渲染模版文件

上面定义好了模版文件之后,可以使用下面的常用方法取解析模版文件,得到模版对象:

func (t *Template) Parse (src string) (*Template, error)
func ParseFiles(filename ..string) (*Template, error)
func ParseGlob(pattern string) (*Template, error)

当然也可以用func New(name string) *Template 函数创建一个名为 name 的模版,然后对其调用上面的方法取解析模版字符串或模版文件。

第三步:模版渲染

渲染模版简单来说就是使用数据去填充模版,当然实际上可能会复杂得多

func (t *Template) Execute(wr io.Writer, data interface{}) error
fucn (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error
基本示例

定义模版文件
我们按照Go模版语法定义一个 hello.tmpl 的模版文件,内容如下:

<!DOCTYPE html>
<html lang="zh-CN">
<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>Hello</title>
</head>
<body>
			<p>Hello {{.}}</p>
</body>
</html>									

解析和渲染模版文件
然后我们创建一个 main.go 文件,在其中写下HTTP server端代码如下:13阿萨德阿萨德

//main.go
package main

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

// main.go
//遇事不决,写注释
func sayHello(w http.ResponseWriter, r *http.Request) {
	// 解析指定文件生成模板对象
	tmpl, err := template.ParseFiles("./hello.tmpl")
	if err != nil {
		fmt.Println("create template failed, err:", err)
		return
	}
	// 利用给定数据渲染模板,并将结果写入w
	name := "小王子"
	err = tmpl.Execute(w, name)
	if err != nil {
		fmt.Println("Execute Failed,err=", err)
		return
	}
}
func main() {
	http.HandleFunc("/", sayHello)
	err := http.ListenAndServe(":9000", nil)
	if err != nil {
		fmt.Println("HTTP server failed,err:", err)
		return
	}
}

注意 ./hello.tmpl 是相对路径,因此要将main.go编译出来再运行,不可以在goland中直接go run,否则会找不到这个hello.tmpl文件
name 就相当于 tmpl文件中的 {{.}} 点.

模版语法
{{.}}

模版语法都包含在{{和}} 中间,其中{{.}}中的点表示当前对象。
当我们传入一个结构体对象时,我们可以根据 . 来访问结构体的对应字段。例如:
定义模版

<!DOCTYPE html>
<html lang="zh-CN">
<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>Hello</title>
</head>
<body>
    <p>Hello {{.Name}}</p>
    <p>性别: {{.Gender}}</p>
    <p>年龄: {{.Age}}</p>
</body>
</html>

解析及渲染模版文件

package main

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

type UserInfo struct{
	Name string
	Gender string
	Age int
}


// main.go
//遇事不决,写注释
func sayHello(w http.ResponseWriter, r *http.Request) {
	// 解析指定文件生成模板对象
	tmpl, err := template.ParseFiles("./hello.tmpl")
	if err != nil {
		fmt.Println("create template failed, err:", err)
		return
	}
	// 利用给定数据渲染模板,并将结果写入w
	user := UserInfo{
		Name:  "小王子",
		Gender:  "男",
		Age:  18,
	}
	err = tmpl.Execute(w, user)
	if err != nil {
		fmt.Println("Execute Failed,err=", err)
		return
	}
}
func main() {
	http.HandleFunc("/", sayHello)
	err := http.ListenAndServe(":9000", nil)
	if err != nil {
		fmt.Println("HTTP server failed,err:", err)
		return
	}
}

同理,当传入的变量是map时,也可以在模版文件中通过 . 根据key来取值

user := map[string]interface{}{
		"Name": "小王子",
		"Gender":  "男",
		"Age":  19,
	}
注释

{{/* a comment */}}
注释,执行时会忽略。可以多行。注释不能嵌套,并且必须紧贴分界符始止

pipeline

pipeline是指产生数据的操作。比如 {{.}} 、{{.Name}}等。Go的模版语法中支持使用管道符号 | 链接多个命令,用法和unix下的管道类似 : | 前面的命令会将运算结果(或返回值)传递给后一个命令的最后一个位置。

注意 :并不是只有使用了 | 才是pipeline。 Go的模版语法中, pipeline概念是传递数据,只要能产生数据的,都是pipeline。

变量

还可以再模版中声明变量,用来保存传入模版的数据或其他语句产生的结果。

$obj := {{.}}

其中 $obj 是变量的名字,在后续的代码中就可以使用该变量了。

移除空格

有时候我们在使用模版语法的时候会不可避免的引入一下空格或者换行符,这样模版最终渲染出来的内容可能就和我们想的不一样,这个时候可以使用 {{- 语法去除模版内容左侧的所有空白符号,使用 -}} 去除模版内容右侧的所有空白符号。
例如:

<p>Hello {{- .u1.Name -}}</p>
注意:- 要紧挨 {{ 和 }} ,同时与模版之间需要使用空格分割。
条件判断

Go模版语法中的条件判断有以下几种:

{{if pipeline}} T1 {{end}}

{{if pipeline}} T1 {{else}} T0 {{end}}

{{if pipeline}} T1 {{else if pipeline}} T0 {{end}}
range

Go的模版语法中使用 range 关键字进行遍历,有以下两种写法,其中pipeline的值必须是数组、切片、字典或者通道。

{{range pipeline}} T1 {{end}}
如果pipeline的值其长度为0,不会有任何输出

{{range pipeline}} T1 {{else}} T0 {{end}}
如果pipeline的值其长度为0,则会执行T0
with

{{with pipeline}} T1 {{end}}
如果pipeline为empty不产生输出,否则将dot设为pipeline的值并执行T1。不修改外面的dot。

{{with pipeline}} T1 {{else}} T0 {{end}}
如果pipeline为empty,不改变dot并执行T0,否则dot设为pipeline的值并执行T1。

预定义函数

执行模版时,函数从两个函数字典中查找:首先是模版函数字典,然后是全局函数字典。一般不在模版内定义函数,而是使用Funcs方法添加函数到模版里。

预定义的全局函数如下:

and
    函数返回它的第一个empty参数或者最后一个参数;
    就是说"and x y"等价于"if x then y else x";所有参数都会执行;
or
    返回第一个非empty参数或者最后一个参数;
    亦即"or x y"等价于"if x then x else y";所有参数都会执行;
not
    返回它的单个参数的布尔值的否定
len
    返回它的参数的整数类型长度
index
    执行结果为第一个参数以剩下的参数为索引/键指向的值;
    如"index x 1 2 3"返回x[1][2][3]的值;每个被索引的主体必须是数组、切片或者字典。
print
    即fmt.Sprint
printf
    即fmt.Sprintf
println
    即fmt.Sprintln
html
    返回与其参数的文本表示形式等效的转义HTML。
    这个函数在html/template中不可用。
urlquery
    以适合嵌入到网址查询中的形式返回其参数的文本表示的转义值。
    这个函数在html/template中不可用。
js
    返回与其参数的文本表示形式等效的转义JavaScript。
call
    执行结果是调用第一个参数的返回值,该参数必须是函数类型,其余参数作为调用该函数的参数;
    如"call .X.Y 1 2"等价于go语言里的dot.X.Y(1, 2);
    其中Y是函数类型的字段或者字典的值,或者其他类似情况;
    call的第一个参数的执行结果必须是函数类型的值(和预定义函数如print明显不同);
    该函数类型值必须有1到2个返回值,如果有2个则后一个必须是error接口类型;
    如果有2个返回值的方法返回的error非nil,模板执行会中断并返回给调用模板执行者该错误;

比较函数

布尔函数会将任何类型的零值视为假,其余视为真。
下面是定义为函数的二元比较运算的集合:

eq 如果arg1 == arg2则返回真
ne 如果arg1 != arg2则返回真
lt 如果arg1 < arg2则返回真
le 如果arg1 <= arg2则返回真
gt 如果arg1 > arg2则返回真
ge 如果arg1 >= arg2则返回真

为了简化多参数相等检测,eq(只有eq)可以接受2个或更多个参数,它会将第一个参数和其余参数依次比较,返回下式的结果:

{{eq arg1 arg2 arg3}}

比较函数只适用于基本类型(或重定义的基本类型,如”type Celsius float32”)。但是,整数和浮点数不能互相比较。

自定义函数

Go的模板支持自定义函数。

package main

import (
	"fmt"
	"html/template"
	"io/ioutil"
	"net/http"
)

type UserInfo struct{
	Name string
	Gender string
	Age int
}

func sayHello (w http.ResponseWriter, r *http.Request){
	//1、定义模版
	htmlByte, err := ioutil.ReadFile("./f.tmpl")
	if err != nil {
		fmt.Println("Read tmpl,err=", err)
		return
	}
	//***自定义一个模版函数
	fun := func(arg UserInfo) (string ,error){
		return  "天理" + arg.Name, nil
	}
	//2、解析模版---采用链式操作在Parse之前调用Funcs添加自定义的fun函数
	tmpl, err := template.New("f.tmpl").Funcs(template.FuncMap{"fun": fun,}).Parse(string(htmlByte))
	if err != nil {
		fmt.Println("create template failed,err=", err)
		return
	}

	user := UserInfo{
		Name: "小王子",
		Gender: "男",
		Age: 18,
	}
	//使用user渲染模版,并将结果写入w
	tmpl.Execute(w,user)
}

func main() {
	http.HandleFunc("/", sayHello)
	err := http.ListenAndServe(":9000", nil)
	if err != nil {
		fmt.Println("HTTP server failed,err:", err)
		return
	}
}


可以在模版文件 hello.tmpl中按照如下方式使用我们自定义的 fun 函数了

{{fun .Name}}
嵌套template

我们可以在template 中嵌套其他的template。这个template 可以是单独的文件,也可以是通过define定义的template
举个例子: t.tmpl 文件内容如下:

<!DOCTYPE html>
<html lang="zh-CN">
<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>tmpl test</title>
</head>
<body>
    
    <h1>测试嵌套template语法</h1>
    <hr>
    {{template "ul.tmpl"}}
    <hr>
    {{template "ol.tmpl"}}
</body>
</html>

{{ define "ol.tmpl"}}
<ol>
    <li>吃饭</li>
    <li>睡觉</li>
    <li>打豆豆</li>
</ol>
{{end}}

ul.tmpl 文件内容如下:

<ul>
    <li>注释</li>
    <li>日志</li>
    <li>测试</li>
</ul>
模版继承 block

{{block “name” pipeline}} T1 {{end}}

block 是定义模版 {{define “name”}} T1 {{end}} 和执行{{template “name” pipeline}} 缩写,典型的用法是定义一组根模版,然后通过在其中重新定义块模版进行自定义。

定义一个基础模版 template/base.tmpl ,内容如下:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <title>Go Template</title>
</head>
<body>
<div class="container-fluid">
    {{block ""content . }}{{end}}
</div>
</body>
</html>

然后定义一个 template/index.tmpl ,继承 base.tmpl :

{{template "base.tmpl"}}

{{define "content"}}
    <div>Hello World</div>
{{end}}

然后使用 template.ParseGlob 按照正则匹配规则解析模版文件,然后通过 ExecuteTemplate 渲染指定的模版:

func index(w http.ResponseWriter, r *http.Request){
	//1、定义模版
	//2、解析模版
	t, err := template.ParseGlob("templates/*.tmpl")
	if err != nil {
		fmt.Println("ParseGlob failed,err=", err)
		return
	}
	//3、渲染模版
	err = t.ExecuteTemplate(w,"index.tmpl", nil)
	if err != nil {
		fmt.Println("execute failed,err=", err)
		return
	}
}

如果我们的模版名称冲突了,例如不同业务线下都定义了一个 index.tmpl 模版,我们可以通过下面两种方法来解决。

  • 1、在模版文件开头使用{{define 模版名}} 语句显式的为模版命名
  • 2、可以把模版文件存放在template文件夹下面的不同目录中,然后使用
template.ParseGlob("template/**/*.tmpl")

解析模版

例子

base.tmpl

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <title>模版继承</title>
    <style>
        * {
            margin: 0;
        }
        .nav {
            height: 50px;
            width: 100%;
            position: fixed;
            top: 0;
            background-color: blanchedalmond;
        }
        .main {
            margin-top:50px;
        }
        .menu {
            width: 20%;
            height: 100%;
            position: fixed;
            left: 0;
            background-color: aqua;
        }
        .center {
            text-align: center;
        }
    </style>
</head>
<body>

<div class="nav"></div>
<div class="main">
    <div class="menu"></div>
    <div class="content center">
        {{block "content" .}}{{end}}
    </div>
</div>

</body>
</html>

home.tmpl

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <title>模版继承</title>
    <style>
        * {
            margin: 0;
        }
        .nav {
            height: 50px;
            width: 100%;
            position: fixed;
            top: 0;
            background-color: blanchedalmond;
        }
        .main {
            margin-top:50px;
        }
        .menu {
            width: 20%;
            height: 100%;
            position: fixed;
            left: 0;
            background-color: aqua;
        }
        .center {
            text-align: center;
        }
    </style>
</head>
<body>

<div class="nav"></div>
<div class="main">
    <div class="menu"></div>
    <div class="content center">
        <h1>这是home页面</h1>
        <p>{{.}}</p>
    </div>
</div>

</body>
</html>

index.tmpl

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <title>模版继承</title>
    <style>
        * {
            margin: 0;
        }
        .nav {
            height: 50px;
            width: 100%;
            position: fixed;
            top: 0;
            background-color: blanchedalmond;
        }
        .main {
            margin-top:50px;
        }
        .menu {
            width: 20%;
            height: 100%;
            position: fixed;
            left: 0;
            background-color: aqua;
        }
        .center {
            text-align: center;
        }
    </style>
</head>
<body>

<div class="nav"></div>
<div class="main">
    <div class="menu"></div>
    <div class="content center">
        <h1>这是home页面</h1>
        <p>{{.}}</p>
    </div>
</div>

</body>
</html>

nhome.tmpl

{{/*继承根模板*/}}

{{template "base.tmpl"}}
{{/*重新定义块模板*/}}
{{define "content"}}
    <h1>这是home页面</h1>
    <p>Hello {{ . }}</p>
{{end}}

nindex.tmpl

{{/*继承根模板*/}}

{{template "base.tmpl"}}
{{/*重新定义块模板*/}}
{{define "content"}}
    <h1>这是index页面</h1>
    <p>Hello {{ . }}</p>
{{end}}

main.go

package main

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

func index(w http.ResponseWriter, r *http.Request){
	//1、定义模版
	//2、解析模版
	t, err := template.ParseFiles("./templates/index.tmpl")
	if err != nil {
		fmt.Println("ParseFiles failed,err=", err)
		return
	}
	//3、渲染模版
	msg := "小王"
	err = t.Execute(w, msg)
	if err != nil {
		fmt.Println("execute failed,err=", err)
		return
	}
}

func home(w http.ResponseWriter, r *http.Request){
	//1、定义模版
	//2、解析模版
	t, err := template.ParseFiles("./templates/home.tmpl")
	if err != nil {
		fmt.Println("ParseFiles failed,err=", err)
		return
	}
	//3、渲染模版
	msg := "大王"
	err = t.Execute(w, msg)
	if err != nil {
		fmt.Println("execute failed,err=", err)
		return
	}
}
func nhome(w http.ResponseWriter, r *http.Request){
	//1、定义模版
	//2、解析模版
	t, err := template.ParseFiles("./templates/base.tmpl", "./templates/nhome.tmpl")
	if err != nil {
		fmt.Println("ParseFiles failed,err=", err)
		return
	}
	//3、渲染模版
	msg := "杨璐羽"
	err = t.ExecuteTemplate(w, "nhome.tmpl", msg)
	if err != nil {
		fmt.Println("execute failed,err=", err)
		return
	}
}

func nindex(w http.ResponseWriter, r *http.Request){
	//1、定义模版
	//2、解析模版
	t, err := template.ParseFiles("./templates/base.tmpl", "./templates/nindex.tmpl")
	if err != nil {
		fmt.Println("ParseFiles failed,err=", err)
		return
	}
	//3、渲染模版
	msg := "田毅"
	err = t.ExecuteTemplate(w, "nindex.tmpl",msg)
	if err != nil {
		fmt.Println("execute failed,err=", err)
		return
	}
}

func main() {
	http.HandleFunc("/home",home)
	http.HandleFunc("/index",index)
	http.HandleFunc("/nhome",nhome)
	http.HandleFunc("/nindex",nindex)
	err := http.ListenAndServe(":9090", nil)
	if err != nil {
		fmt.Println("Listen failed,err=", err)
		return
	}
}
修改默认的标识符

Go标准库的模版引擎使用的花括号{{和 }}作为标识,而许多前端框架 (如 vue 和 AngularJS)也使用{{}}作为标识符,所以当我们同时使用Go语言模版引擎和以上前端框架时就会发生冲突,这个时候我们就需要修改标识符,修改前端的或者修改Go语言的。这里演示如何修改go语言模版引擎默认的标识符:
G0第21章 :gin框架介绍、RESTful API、Gin渲染

template.New("test").Delims("{[" , "]}").ParseFiles("./t.tmpl")

text/tempalte 与 html/template的区别

后者 针对的是需要返回HTML内容的场景,在模版渲染过程中会对一些有风险的内容进行转义,以此来防范跨站脚本攻击。

例如,我定义下面的模版文件

<!DOCTYPE html>
<html lang="zh-CN">
<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>Hello</title>
</head>
<body>
    {{.}}
</body>
</html>

这个时候传入一段JS代码并使用html/template 去渲染改文件,会在页面上显示出转义后的JS内容。
这就是html/template为我们做的事。 这就是html/template为我们做的事。
但是在某些场景下,我们如果相信用户输入的内容,不想转义的话,可以自行编写一个safe函数,手动返回一个 template.HTML 类型的内容,示例如下:

func xss(w http.ResponseWriter, r *http.Request){
	tmpl,err := template.New("xss.tmpl").Funcs(template.FuncMap{
		"safe": func(s string)template.HTML {
			return template.HTML(s)
		},
	}).ParseFiles("./xss.tmpl")
	if err != nil {
		fmt.Println("create template failed, err:", err)
		return
	}
	jsStr := `<script>alert('嘿嘿嘿')</script>`
	err = tmpl.Execute(w, jsStr)
	if err != nil {
		fmt.Println(err)
	}
}

这样我们只需要在模板文件不需要转义的内容后面使用我们定义好的safe函数就可以了。

{{ . | safe }}
示例
<!DOCTYPE html>
<html lang="zh-CN">
<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>xss</title>
</head>
<body>
{{ .str1 }}
{{ safe .str2 }}
{{/*or*/}}
{{ .str2 | safe }}
</body>
</html>

package main

import (
	"fmt"
	"html/template"
	"io/ioutil"
	"net/http"
)

type UserInfo struct{
	Name string
	Age int
	Gender string
}

func sayHello (w http.ResponseWriter, r *http.Request){
	//1、定义模版
	htmlByte, err := ioutil.ReadFile("./f.tmpl")
	if err != nil {
		fmt.Println("Read tmpl,err=", err)
		return
	}
	//***自定义一个模版函数
	fun := func(arg UserInfo) (string ,error){
		return  "天理" + arg.Name, nil
	}
	//2、解析模版---采用链式操作在Parse之前调用Funcs添加自定义的fun函数
	tmpl, err := template.New("f.tmpl").Funcs(template.FuncMap{"fun": fun,}).Parse(string(htmlByte))
	if err != nil {
		fmt.Println("create template failed,err=", err)
		return
	}

	user := UserInfo{
		Name: "小王子",
		Gender: "男",
		Age: 18,
	}
	//使用user渲染模版,并将结果写入w
	tmpl.Execute(w,user)
}
func tmplDemo(w http.ResponseWriter, r *http.Request){
	//1、定义模版
	//2、解析模版
	t, err := template.ParseFiles("./t.tmpl", "./ul.tmpl")
	if err != nil {
		fmt.Println("Parse failed,err=", err)
		return
	}
	//3、渲染模版
	user := UserInfo{
		Name: "小王子",
		Age: 18,
		Gender: "男",
	}
	t.Execute(w,user)
}

func xss (w http.ResponseWriter, r *http.Request){
	//1、定义模版
	//2、解析模版
	//t, err := template.ParseFiles("./xss.tmpl")
	//if err != nil {
	//	fmt.Println("Parse Failed,err=", err)
	//	return
	//}
	//解析模版之前定义一个自定义函数safe
	t, err := template.New("xss.tmpl").Funcs(template.FuncMap{
		"safe": func(str string)template.HTML {
			return template.HTML(str)
		},
	}).ParseFiles("./xss.tmpl")
	//3、渲染模版
	str1 := "<script>alert(123);</script>"
	str2 := "<a href='http://liwenzhou.com'>liwenzhou的博客</a>"
	err = t.Execute(w, map[string]interface{}{
		"str1": str1,
		"str2": str2,
	})
	if err != nil {
		fmt.Println("Execute Failed,err=", err)
		return
	}
}

func main() {
	http.HandleFunc("/say", sayHello)
	http.HandleFunc("/tmpl", tmplDemo)
	http.HandleFunc("/xss",xss)
	err := http.ListenAndServe(":9000", nil)
	if err != nil {
		fmt.Println("HTTP server failed,err:", err)
		return
	}
}

2、HTML渲染

首先定义一个存放模版文件的templates文件夹,然后在其内部按照业务分别定义一个posts文件夹和一个users文件夹。

posts/index.html文件的内容如下:


{{define "posts/index.html"}}
<!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}}


users/index.html文件的内容如下:
{{define "users/index.html"}}
<!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}}
Gin框架中使用LoadHTMLGlob()或者LoadHTMLFiles()方法进行HTML模板渲染。
func main() {
	r := gin.Default()
	r.LoadHTMLGlob("templates/**/*")
	//r.LoadHTMLFiles("templates/posts/index.html", "templates/users/index.html")
	r.GET("/posts/index", func(c *gin.Context) {
		c.HTML(http.StatusOK, "posts/index.html", gin.H{
			"title": "posts/index",
		})
	})

	r.GET("users/index", func(c *gin.Context) {
		c.HTML(http.StatusOK, "users/index.html", gin.H{
			"title": "users/index",
		})
	})

	r.Run(":8080")
}

3、自定义模板函数

定义一个不转义相应内容的safe模板函数如下:
func main() {
	router := gin.Default()
	router.SetFuncMap(template.FuncMap{
		"safe": func(str string) template.HTML{
			return template.HTML(str)
		},
	})
	router.LoadHTMLFiles("./index.tmpl")

	router.GET("/index", func(c *gin.Context) {
		c.HTML(http.StatusOK, "index.tmpl", "<a href='https://liwenzhou.com'>李文周的博客</a>")
	})

	router.Run(":8080")
}
在index.tmpl中使用定义好的safe模板函数:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <title>修改模板引擎的标识符</title>
</head>
<body>
<div>{{ . | safe }}</div>
</body>
</html>

4、静态文件处理

静态文件是指 .css .js 文件 图片

当我们渲染的HTML文件中引用了静态文件时,我们只需要按照以下方式在渲染页面前调用 gin.Static 方法即可。
func main(){
	r := gin.Default()
	r.Static("/static", "./static")
	r.LoadHttpGlob("template/**/*")
	// ...
	r.Run(":9090")
}

5、使用模版继承

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

然后我们定义一个loadTemplates函数如下:

func loadTemplate(templatesDir string) multitemplate.Renderer {
r := multitemplate.NewRenderer()
layouts, err := filepath.Glob(templatesDir + "/layouts/*.tmpl")
if err != nil {
	panic(err.Error())
}
includes,err := filepath.Glob(templatesDir + "/includes/*.tmpl")
if err != nil{
	panic(err.Error())
}
//为layouts/和includes/目录生成 templates map
for _, include := range includes {
	layoutCopy := make([]string, len(layouts))
	copy(layoutCopy, layouts)
	files := append(layoutCopy, include)
	r.AddFromFiles(filepath.Base(include), files...)
	return r
}
}

我们在main函数中

func indexFunc(c *gin.Context) {
	c.HTML(http.StatusOK, "index.tmpl", nil)
}
func homeFunc(c *gin.Context) {
	c.HTML(http.StatusOK, "home.tmpl", nil)
}

func main () {
	r := gin.Default
	r.HTMLRender = loadTemplate("./templates")
	r.GET("/index", indexFunc)
	r.GET("/home",homeFunc)
	r.Run()
}

6、补充文件路径处理

关于模版文件和静态文件的路径,我们需要根据项目的要求进行设置。可以使用下面的函数获取当前执行程序的路径。文章来源地址https://www.toymoban.com/news/detail-460359.html

func getCurentPath() string{
	if ex, err := os.Executable(); err == nil {
			return filepath.Dir(ex)
	}
	return "./"
}

7、JSON渲染

package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
	r := gin.Default()

	//gin.H 是map[string]interface{}的缩写
	r.GET("/someJSON", func(c *gin.Context){
		//方式一:自己拼接JSON
		data := gin.H{"message": "Hello world!","age": 18, "name": "小王子"}
		c.JSON(http.StatusOK, data)
	})
	r.GET("/moreJSON", func(c *gin.Context){
		//方式二:使用结构体。要灵活使用tag来对结构体字段做定制化操作,比如需要小写的时候
		var msg struct{
			Name string `json:"user"` // josn的序列化是通过反射原理,小写不可导出。
			Message string
			Age int
		}
		msg.Name = "小王子"
		msg.Message = "Hello world!"
		msg.Age = 18
		c.JSON(http.StatusOK, msg)
	})

	r.Run(":8080")
}

8、XML渲染

func main() {
	r := gin.Default()
	// gin.H 是map[string]interface{}的缩写
	r.GET("/someXML", func(c *gin.Context) {
		// 方式一:自己拼接JSON
		c.XML(http.StatusOK, gin.H{"message": "Hello world!"})
	})
	r.GET("/moreXML", func(c *gin.Context) {
		// 方法二:使用结构体
		type MessageRecord struct {
			Name    string
			Message string
			Age     int
		}
		var msg MessageRecord
		msg.Name = "小王子"
		msg.Message = "Hello world!"
		msg.Age = 18
		c.XML(http.StatusOK, msg)
	})
	r.Run(":8080")
}

9、YMAL 渲染

r.GET("/someYAML", func(c *gin.Context) {
	c.YAML(http.StatusOK, gin.H{"message": "ok", "status": http.StatusOK})
})

10、protobuf渲染

r.GET("/someProtoBuf", func(c *gin.Context) {
	reps := []int64{int64(1), int64(2)}
	label := "test"
	// protobuf 的具体定义写在 testdata/protoexample 文件中。
	data := &protoexample.Test{
		Label: &label,
		Reps:  reps,
	}
	// 请注意,数据在响应中变为二进制数据
	// 将输出被 protoexample.Test protobuf 序列化了的数据
	c.ProtoBuf(http.StatusOK, data)
})

到了这里,关于G0第21章 :gin框架介绍、RESTful API、Gin渲染的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • Gin 框架介绍与快速入门

    Gin 框架介绍与快速入门

    目录 Gin 框架介绍与快速入门 一、Gin框架介绍 1. 快速和轻量级 2. 路由和中间件 3. JSON解析 4. 支持插件 5. Gin相关文档 二、基本使用 1.安装 2.导入 3.第一个Gin 应用 三、应用举例 四、Gin 入门核心 1.gin.Engine 2.gin.Context Gin是一个轻量级的Go语言Web框架,它具有高性能和简洁的设计

    2024年02月03日
    浏览(10)
  • gin渲染篇

    gin渲染篇

    json、结构体、XML、YAML类似于java的properties、ProtoBuf gin支持加载HTML模板, 然后根据模板参数进行配置并返回相应的数据,本质上就是字符串替换 LoadHTMLGlob()方法可以加载模板文件 目录结构: 如果你的目录结构是下面的情况 代码如下: 如果你想进行头尾分离就是下面这种写法了

    2024年01月21日
    浏览(11)
  • Gin学习记录3——模版与渲染

    Gin学习记录3——模版与渲染

    如果只是想返回数据,可以使用以下函数: 例如: 则会返回一个 JSON 使用模版前,需要载入模版: 使用 func (c *Context) HTML(code int, name string, obj any) 即可渲染: 每个模版的开始与结束需要使用 {{ define }} {{ end }} 来定义模版名称,比如: 载入的时候需要载入全部模版,调用时,

    2024年02月09日
    浏览(7)
  • gin 框架中的 gin.Context

    Context 是 gin 中最重要的部分。 例如,它允许我们在中间件之间传递变量、管理流程、验证请求的 JSON 并呈现 JSON 响应。 Context 中封装了原生的 Go HTTP 请求和响应对象,同时还提供了一些方法,用于获取请求和响应的信息、设置响应头、设置响应状态码等操作。 在 Gin 中,C

    2024年02月14日
    浏览(17)
  • Gin之gin介绍和安装

    1.1 gin 是什么? Gin 是一个用 Go (Golang) 编写的 HTTP web 框架。 它是一个类似于 martini 但拥有更好性能的 API 框架,由于 httprouter,速度提高了近 40 倍。如果你需要极好的性能,使用 Gin 吧。 https://github.com/go-martini/martini https://github.com/julienschmidt/httprouter 1.2 gin优点? 快速 基于 R

    2024年01月21日
    浏览(11)
  • Gin-swaggo为gin框架提供Swagger 文档

    官方: https://github.com/swaggo/gin-swagger 开始使用 为API方法增加注释,加在controller(api)层, See Declarative Comments Format. 运行下面命令下载swgo: Go 1.17后的版本, 使用  go get 安装可执行文件已被废弃 . 用 go install代替 : 在你的go项目根目录运行swga(上文下载的exe)(比方说  ~/root/go-project-nam

    2024年02月09日
    浏览(28)
  • 【Gin框架】框架入门

    【Gin框架】框架入门

    Gin 是一个Go (Golang) 编写的轻量级 http web 框架,运行速度非常快,如果你是性能和高效的追求者,我们推荐你使用Gin 框架。 Gin 最擅长的就是Api 接口的高并发,如果项目的规模不大,业务相对简单,这个时候我们也推荐您使用Gin。 当某个接口的性能遭到较大挑战的时候,这个

    2024年02月02日
    浏览(8)
  • 解决GO安装gin框架(go get -u github.com/gin-gonic/gin)超时问题

    解决GO安装gin框架(go get -u github.com/gin-gonic/gin)超时问题

    🍊gin框架github地址:https://github.com/gin-gonic/gin         按照官方文档安装gin,但是尝试了好几次,包括使用国内网络或者使用梯子,都超时失败了,爆了如下超时错误 🍊 解决方法如下 1、先查看go相关的配置 如上显示GOPROXY配置是https://proxy.golang.org,这个地址已经被墙了,

    2024年02月11日
    浏览(14)
  • [golang gin框架] 39.Gin商城项目-微服务实战之微服务架构

    [golang gin框架] 39.Gin商城项目-微服务实战之微服务架构

    单体架构在 中小企业内部 用的是非常多的,当 业务不复杂 , 团队规模不大 的时候,单体架构比微服务架构具有 更高的生产率 单体架构 当 业务比较复杂 , 并发量 比较大, 团队规模扩大的时候, 就需要引入微服务架构了,它比单体架构 具有 更高的生产率, 可以 节省成本 , 解

    2024年02月12日
    浏览(24)
  • Go(四)gin框架

    1.1、下载和安装gin 下载包:go get github.com/gin-gonic/gin 使用go mod管理包: 1)初始化 Go Modules :go mod init your_module_name,这将创建一个 go.mod 文件,记录你的项目的模块信息和当前依赖关系; 2)复制依赖包到vendor目录 :\\\"go mod vendor\\\" 会将项目的所有包复制到vendor目录中。这包括

    2024年01月25日
    浏览(14)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包