1.Gin介绍
Gin 是一个用 Go (Golang) 编写的 HTTP Web 框架。 它具有类似 Martini 的 API,但性能比 Martini 快 40 倍。如果你需要极好的性能,使用 Gin 吧
下载gin:
go get -u github.com/gin-gonic/gin
将gin引入到代码中:
import "github.com/gin-gonic/gin"
2.第一个gin网站
示例
以下展示一个最小的gin web程序
package main import ( "github.com/gin-gonic/gin" ) // hello 路径的函数 func sayHello(c *gin.Context) { c.JSON(200, gin.H{ "message": "hello gin!", }) // 返回一个JSON数据 } func main() { r := gin.Default() // 返回默认的路由引擎 // 指定用户使用GET请求访问/hello时,执行sayHello函数 r.GET("hello/", sayHello) // 启动服务 r.Run() }
启动程序之后,浏览器访问:
输出信息:
{"message":"hello gin!"}
RESTful API
REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”或“表现层状态转化”。
简单来说,REST的含义就是客户端与Web服务器之间进行交互的时候,使用HTTP协议中的4个请求方法代表不同的动作。
GET用来获取资源POST用来新建资源PUT用来更新资源DELETE用来删除资源。只要API程序遵循了REST风格,那就可以称其为RESTful API。目前在前后端分离的架构中,前后端基本都是通过RESTful API来进行交互。
例如,我们现在要编写一个管理书籍的系统,我们可以查询对一本书进行查询、创建、更新和删除等操作,我们在编写程序的时候就要设计客户端浏览器与我们Web服务端交互的方式和路径。按照经验我们通常会设计成如下模式:
同样的需求我们按照RESTful API设计如下:
开发RESTful API的时候我们通常使用Postman来作为客户端的测试工具🥖
代码:
package main import ( "github.com/gin-gonic/gin" ) func main() { r := gin.Default() // 返回默认的路由引擎 // 指定用户使用GET请求访问/hello时,执行函数 r.GET("hello/", func(c *gin.Context) { c.JSON(200, gin.H{ "method": "GET", }) }) // RESTful风格 API r.POST("hello/", func(c *gin.Context) { c.JSON(200, gin.H{ "method": "POST", }) }) r.PUT("hello/", func(c *gin.Context) { c.JSON(200, gin.H{ "method": "PUT", }) }) r.DELETE("hello/", func(c *gin.Context) { c.JSON(200, gin.H{ "method": "DELETE", }) }) // 启动服务 r.Run() }
3.模板渲染
html/template包实现了数据驱动的模板,用于生成可防止代码注入的安全的HTML内容。它提供了和text/template包相同的接口,Go语言中输出HTML的场景都应使用html/template这个包
模板与渲染
在一些前后端不分离的Web架构中,我们通常需要在后端将一些数据渲染到HTML文档中,从而实现动态的网页(网页的布局和样式大致一样,但展示的内容并不一样)效果。
我们这里说的模板可以理解为事先定义好的HTML文档文件,模板渲染的作用机制可以简单理解为文本替换操作–使用相应的数据去替换HTML文档中事先准备好的标记。
很多编程语言的Web框架中都使用各种模板引擎,比如Python语言中Flask框架中使用的jinja2模板引擎。
Go语言的模板引擎
Go语言内置了文本模板引擎text/template和用于HTML文档的html/template。它们的作用机制可以简单归纳如下:
模板文件通常定义为.tmpl和.tpl为后缀(也可以使用其他的后缀),必须使用UTF8编码。
模板文件中使用{{和}}包裹和标识需要传入的数据。
传给模板这样的数据就可以通过点号(.)来访问,如果数据是复杂类型的数据,可以通过{{ .FieldName }}来访问它的字段。
除{{和}}包裹的内容外,其他内容均不做修改原样输出。
模板引擎的使用
创建的模板文件:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>hint</title> </head> <body> <h1>Hello {{ . }}</h1> </body> </html>
解析模板:
// 解析模板 // 当前路径下的tmpl t, err := template.ParseFiles("./tem.tmpl") if err != nil { fmt.Println("ParseFiles failed error:%v", err) return }
渲染模板:
// 渲染模板 err = t.Execute(w, "大河之犬") if err != nil { fmt.Println("tmpl Execute failed err=%v", err) }
后端文件:
package main import ( "fmt" "html/template" "net/http" ) func sayHello(w http.ResponseWriter, r *http.Request) { // 解析模板 // 当前路径下的tmpl t, err := template.ParseFiles("./tem.tmpl") if err != nil { fmt.Println("ParseFiles failed error:%v", err) return } // 渲染模板 err = t.Execute(w, "大河之犬") if err != nil { fmt.Println("tmpl Execute failed err=%v", err) } } func main() { // http服务 http.HandleFunc("/", sayHello) // 使用9000端口 err := http.ListenAndServe(":9000", nil) if err != nil { fmt.Println("HTTP server start failed , err %v", err) return } }
打开浏览器,可成功渲染:
4.模板语法
模板渲染结构体
type User struct { Name string Sex string Age int } func sayHello(w http.ResponseWriter, r *http.Request) { // 解析模板 // 当前路径下的tmpl t, err := template.ParseFiles("./tem.tmpl") if err != nil { fmt.Println("ParseFiles failed error:%v", err) return } // 渲染模板 u1 := User{ Name: "dahe", // 别人可以访问时,首字母要大写 Sex: "男", Age: 18, } err = t.Execute(w, u1) if err != nil { fmt.Println("tmpl Execute failed err=%v", err) } }
模板文件:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>hint</title> </head> <body> <h1>Hello {{ .Name }}</h1> <h1>Sex {{ .Sex }}</h1> <h1>Age {{ .Age }}</h1> </body> </html>
同样的,模板渲染map:
func sayHello(w http.ResponseWriter, r *http.Request) { // 解析模板 // 当前路径下的tmpl t, err := template.ParseFiles("./tem.tmpl") if err != nil { fmt.Println("ParseFiles failed error:%v", err) return } // 渲染模板 m1 := map[string]interface{}{ "Name": "dahe", // map无需首字母大写 "Sex": "女", "Age": "20", } err = t.Execute(w, m1) if err != nil { fmt.Println("tmpl Execute failed err=%v", err) } }
模板传递多个参数
func sayHello(w http.ResponseWriter, r *http.Request) { // 解析模板 // 当前路径下的tmpl t, err := template.ParseFiles("./tem.tmpl") if err != nil { fmt.Println("ParseFiles failed error:%v", err) return } // 渲染模板 u1 := User{ Name: "wangwei", Sex: "男", Age: 18, } m1 := map[string]interface{}{ "Name": "dahe", // map无需首字母大写 "Sex": "女", "Age": "20", } err = t.Execute(w, map[string]interface{}{ "u1": u1, "m1": m1, }) if err != nil { fmt.Println("tmpl Execute failed err=%v", err) } }
模板文件:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>hint</title> </head> <body> <h1>Hello {{ .u1.Name }}</h1> <h1>Sex {{ .u1.Sex }}</h1> <h1>Age {{ .u1.Age }}</h1> <h1>Hello {{ .m1.Name }}</h1> <h1>Sex {{ .m1.Sex }}</h1> <h1>Age {{ .m1.Age }}</h1> </body> </html>
成功渲染:
注释
{{/* a comment */}} 注释,执行时会忽略。可以多行。注释不能嵌套,并且必须紧贴分界符始止。
变量
{{ $hello := "ai" }} {{ $tempSex := .m1.Sex }} {{ $hello }} {{ $tempSex }}
移除空格
有时候我们在使用模板语法的时候会不可避免的引入一下空格或者换行符,这样模板最终渲染出来的内容可能就和我们想的不一样,这个时候可以使用{{-
语法去除模板内容左侧的所有空白符号, 使用-}}
去除模板内容右侧的所有空白符号。
{{- .Name -}}
条件判断
Go模板语法中的条件判断有以下几种:
{{if pipeline}} T1 {{end}} {{if pipeline}} T1 {{else}} T0 {{end}} {{if pipeline}} T1 {{else if pipeline}} T0 {{end}}
例如:
{{ if eq .m1.Name "dahe" }} <h2>你好啊,dahe</h2> {{ end }}
比较函数
布尔函数会将任何类型的零值视为假,其余视为真。
下面是定义为函数的二元比较运算的集合:
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}}
range
我们在后端传入一个切片:
hobbyList := []string{ "C++", "Java", "Go", } m1 := map[string]interface{}{ "Name": "dahe", // map无需首字母大写 "Sex": "女", "Age": "20", "Hobby": hobbyList, }
模板使用range进行渲染:
{{ range $idx,$hobby := .m1.Hobby }} <p>{{$idx}} - {{$hobby}}</p> {{ end }}
range-else:
{{ range $idx,$hobby := .m1.Hobby }} <p>{{$idx}} - {{$hobby}}</p> {{else}} 没有爱好 {{ end }}
index取值:
{{with .m1}} <p>{{ index .Hobby 2 }}</p> <!--取第三个元素--> {{end}}
with
简化模板语言变量的使用,例如,局部使用.m1值
{{with .m1}} <p>{{.Name}}</p> <p>{{.Age}}</p> {{end}}