Gin框架入门实战系列教程之Gin环境搭建 Gin程序的热加载 Gin路由 GET POST PUT DELETE
主讲教师:(大地) 在线文档见网盘下载:
百度网盘 请输入提取码 提取码:abcd
一、Gin介绍
Gin 是一个 Go (Golang) 编写的轻量级http web 框架,运行速度非常快,如果你是性能和高效的追求者,我们推荐你使用Gin框架。
Gin最擅长的就是Api接口的高并发,如果项目的规模不大,业务相对简单,这个时候我们也推荐您使用Gin。 当某个接口的性能遭到较大挑战的时候,这个还是可以考虑使用Gin重写接口。
Gin也是一个流行的golang Web框架,Github Strat量已经超过了50k。
Gin的官网:Gin Web Framework Gin Github地址:GitHub - gin-gonic/gin: Gin is a HTTP web framework written in Go (Golang). It features a Martini-like API with much better performance -- up to 40 times faster. If you need smashing performance, get yourself some Gin.
二、Gin环境搭建
要安装 Gin 软件包,需要先安装 Go 并设置 Go 工作区。
1.下载并安装 gin:
$ go get -u github.com/gin-gonic/gin
2.将 gin 引入到代码中:
import "github.com/gin-gonic/gin"
3.(可选)如果使用诸如 http.StatusOK 之类的常量,则需要引入 net/http 包:
import "net/http"
4、新建Main.go配置路由
- package main
-
- import (
- "github.com/gin-gonic/gin"
- )
-
- func main() {
- // 创建一个默认的路由引擎
- r := gin.Default()
- // 配置路由
- r.GET("/", func(c *gin.Context) {
- c.JSON(200, gin.H{ // c.JSON:返回JSON格式的数据
- "message": "Hello world!",
- })
- })
- // 启动HTTP服务,默认在0.0.0.0:8080启动服务
- r.Run()
- }
5、运行你的项目
$ go run main.go
6、要改变默认启动的端口
r.Run(":9000")
如果go get失败请参考: Golang Beego中没法下载第三方包解决办法
三、golang程序的热加载
所谓热加载就是当我们对代码进行修改时,程序能够自动重新加载并执行,这在我们开发中是非常便利的,可以快速进行代码测试,省去了每次手动重新编译
beego中我们可以使用官方给我们提供的bee工具来热加载项目,但是gin中并没有官方提供的热加载工具,这个时候我们要实现热加载就可以借助第三方的工具。
- go get github.com/pilu/fresh
- D:\gin_demo>fresh
工具2:GitHub - codegangsta/gin: Live reload utility for Go web servers
- go get -u github.com/codegangsta/gin
- D:\gin_demo>gin run main.go
四、Gin框架中的路由
4.1、路由概述
路由(Routing)是由一个 URI(或者叫路径)和一个特定的 HTTP 方法(GET、POST 等)组成的,涉及到应用如何响应客户端对某个网站节点的访问。
RESTful API是目前比较成熟的一套互联网应用程序的API设计理论,所以我们设计我们的路由的时候建议参考RESTful API指南。
在RESTful架构中,每个网址代表一种资源,不同的请求方式表示执行不同的操作: GET(SELECT) 从服务器取出资源(一项或多项) POST(CREATE) 在服务器新建一个资源 PUT(UPDATE) 在服务器更新资源(客户端提供改变后的完整资源) DELETE(DELETE) 从服务器删除资源
4.2、简单的路由配置
简单的路由配置(可以通过postman测试) 当用GET请求访问一个网址的时候,做什么事情:
- r.GET("网址", func(c *gin.Context) {
-
- c.String(200, "Get")
-
- })
当用POST访问一个网址的时候,做什么事情:
- r.POST("网址", func(c *gin.Context) {
- c.String(200, "POST")
- })
当用PUT访问一个网址的时候,执行的操作:
- r.PUT("网址", func(c *gin.Context) {
- c.String(200, "PUT")
- })
当用DELETE访问一个网址的时候,执行的操作:
- r.DELETE("网址", func(c *gin.Context) {
- c.String(200, "DELETE")
- })
路由里面获取Get传值 域名/news?aid=20
- r.GET("/news", func(c *gin.Context) {
- aid := c.Query("aid")
- c.String(200, "aid=%s", aid)
- })
动态路由 域名/user/20
- r.GET("/user/:uid", func(c *gin.Context) {
- uid := c.Param("uid")
- c.String(200, "userID=%s", uid)
- })
-
4.3、 c.String() c.JSON() c.JSONP() c.XML() c.HTML()
返回一个字符串
- r.GET("/news", func(c *gin.Context) {
- aid := c.Query("aid")
- c.String(200, "aid=%s", aid)
- })
返回一个JSON数据
- func main() {
- r := gin.Default()
-
- // gin.H 是map[string]interface{}的缩写
- r.GET("/someJSON", func(c *gin.Context) {
- // 方式一:自己拼接JSON
- c.JSON(http.StatusOK, gin.H{"message": "Hello world!"})
- })
- r.GET("/moreJSON", func(c *gin.Context) {
- // 方法二:使用结构体
- var msg struct {
- Name string `json:"user"`
- Message string
- Age int
- }
- msg.Name = "IT营学院"
- msg.Message = "Hello world!"
- msg.Age = 18
- c.JSON(http.StatusOK, msg)
- })
- r.Run(":8080")
- }
JSOPN
- func main() {
- r := gin.Default()
-
- r.GET("/JSONP", func(c *gin.Context) {
- data := map[string]interface{}{
- "foo": "bar",
- }
-
- // /JSONP?callback=x
- // 将输出:x({\"foo\":\"bar\"})
- c.JSONP(http.StatusOK, data)
- })
-
- // 监听并在 0.0.0.0:8080 上启动服务
- r.Run(":8080")
- }
返回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 = "IT营学院"
- msg.Message = "Hello world!"
- msg.Age = 18
- c.XML(http.StatusOK, msg)
- })
- r.Run(":8080")
- }
渲染模板
- router.GET("/", func(c *gin.Context) {
- c.HTML(http.StatusOK, "default/index.html", map[string]interface{}{
- "title": "前台首页"
- })
- })
五、Gin HTML模板渲染
5.1、全部模板放在一个目录里面的配置方法
1、我们首先在项目根目录新建templates文件夹,然后在文件夹中新建index.html
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Document</title>
- </head>
- <body>
- <h1>这是一个html模板</h1>
-
- <h3>{{.title}}</h3>
- </body>
- </html>
2、Gin框架中使用c.HTML可以渲染模板,渲染模板前需要使用LoadHTMLGlob()或者LoadHTMLFiles()方法加载模板。
- router.GET("/", func(c *gin.Context) {
- c.HTML(http.StatusOK, "default/index.html", map[string]interface{}{
- "title": "前台首页"
- })
- })
- router.GET("/", func(c *gin.Context) {
- c.HTML(http.StatusOK, "index.html", gin.H{
- "title": "Main website",
- })
- })
- package main
-
- import (
- "net/http"
-
- "github.com/gin-gonic/gin"
- )
-
- func main() {
- router := gin.Default()
- router.LoadHTMLGlob("templates/*")
- //router.LoadHTMLFiles("templates/template1.html", "templates/template2.html")
- router.GET("/", func(c *gin.Context) {
- c.HTML(http.StatusOK, "index.html", gin.H{
- "title": "Main website",
- })
- })
- router.Run(":8080")
- }
5.2、模板放在不同目录里面的配置方法
Gin框架中如果不同目录下面有同名模板的话我们需要使用下面方法加载模板 注意:定义模板的时候需要通过define定义名称 templates/admin/index.html
- <!-- 相当于给模板定义一个名字 define end 成对出现-->
- {{ define "admin/index.html" }}
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Document</title>
- </head>
- <body>
- <h1>后台模板</h1>
-
- <h3>{{.title}}</h3>
- </body>
- </html>
- {{ end }}
templates/default/index.html
- <!-- 相当于给模板定义一个名字 define end 成对出现-->
- {{ define "default/index.html" }}
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Document</title>
- </head>
- <body>
- <h1>前台模板</h1>
- <h3>{{.title}}</h3>
- </body>
- </html>
- {{end}}
业务逻辑
- package main
-
- import (
- "net/http"
-
- "github.com/gin-gonic/gin"
- )
-
- func main() {
- router := gin.Default()
- router.LoadHTMLGlob("templates/**/*")
- router.GET("/", func(c *gin.Context) {
- c.HTML(http.StatusOK, "default/index.html", gin.H{
- "title": "前台首页",
- })
- })
- router.GET("/admin", func(c *gin.Context) {
- c.HTML(http.StatusOK, "admin/index.html", gin.H{
- "title": "后台首页",
- })
- })
- router.Run(":8080")
- }
注意:如果模板在多级目录里面的话需要这样配置r.LoadHTMLGlob(“templates///*”) /**表示目录
5.3、gin模板基本语法
1、{{.}} 输出数据 模板语法都包含在{{和}}中间,其中{{.}}中的点表示当前对象。 当我们传入一个结构体对象时,我们可以根据.来访问结构体的对应字段。例如: 业务逻辑
- package main
-
- import (
- "net/http"
-
- "github.com/gin-gonic/gin"
- )
-
- type UserInfo struct {
- Name string
- Gender string
- Age int
- }
-
- func main() {
- router := gin.Default()
- router.LoadHTMLGlob("templates/**/*")
- user := UserInfo{
- Name: "张三",
- Gender: "男",
- Age: 18,
- }
- router.GET("/", func(c *gin.Context) {
- c.HTML(http.StatusOK, "default/index.html", map[string]interface{}{
- "title": "前台首页",
- "user": user,
- })
- })
- router.Run(":8080")
- }
-
模板
- <!-- 相当于给模板定义一个名字 define end 成对出现-->
- {{ define "default/index.html" }}
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Document</title>
- </head>
- <body>
- <h1>前台模板</h1>
-
- <h3>{{.title}}</h3>
-
- <h4>{{.user.Name}}</h4>
- <h4>{{.user.Age}}</h4>
- </body>
- </html>
- {{end}}
2、注释
{{/* a comment */}}
注释,执行时会忽略。可以多行。注释不能嵌套,并且必须紧贴分界符始止。 3、变量 我们还可以在模板中声明变量,用来保存传入模板的数据或其他语句生成的结果。具体语法如下:
- <h4>{{$obj := .title}}</h4>
-
- <h4>{{$obj}}</h4>
-
4、移除空格 有时候我们在使用模板语法的时候会不可避免的引入一下空格或者换行符,这样模板最终渲染出来的内容可能就和我们想的不一样,这个时候可以使用{{-语法去除模板内容左侧的所有空白符号, 使用-}}去除模板内容右侧的所有空白符号。 例如:
{{- .Name -}}
注意:-要紧挨{{和}},同时与模板值之间需要使用空格分隔。
5、比较函数 布尔函数会将任何类型的零值视为假,其余视为真。 下面是定义为函数的二元比较运算的集合: eq 如果arg1 == arg2则返回真 ne 如果arg1 != arg2则返回真 lt 如果arg1 < arg2则返回真 le 如果arg1 <= arg2则返回真 gt 如果arg1 > arg2则返回真 ge 如果arg1 >= arg2则返回真
6、条件判断 Go模板语法中的条件判断有以下几种:
- {{if pipeline}} T1 {{end}}
-
- {{if pipeline}} T1 {{else}} T0 {{end}}
-
- {{if pipeline}} T1 {{else if pipeline}} T0 {{end}}
-
-
- {{if gt .score 60}}
- 及格
- {{else}}
- 不及格
- {{end}}
-
-
- {{if gt .score 90}}
- 优秀
- {{else if gt .score 60}}
- 及格
- {{else}}
- 不及格
- {{end}}
6、range Go的模板语法中使用range关键字进行遍历,有以下两种写法,其中pipeline的值必须是数组、切片、字典或者通道。
- {{range $key,$value := .obj}}
- {{$value}}
- {{end}}
如果pipeline的值其长度为0,不会有任何输出
- {{range $key,$value := .obj}}
- {{$value}}
- {{else}}
- pipeline的值其长度为0
- {{end}}
如果pipeline的值其长度为0,则会执行T0。
- router.GET("/", func(c *gin.Context) {
- c.HTML(http.StatusOK, "default/index.html", map[string]interface{}{
- "hobby": []string{"吃饭", "睡觉", "写代码"},
- })
- })
- {{range $key,$value := .hobby}}
- <p>{{$value}}</p>
- {{end}}
7、With
- user := UserInfo{
- Name: "张三",
- Gender: "男",
- Age: 18,
- }
-
- router.GET("/", func(c *gin.Context) {
- c.HTML(http.StatusOK, "default/index.html", map[string]interface{}{
-
- "user": user,
-
- })
- })
以前要输出数据:
- <h4>{{.user.Name}}</h4>
- <h4>{{.user.Gender}}</h4>
- <h4>{{.user.Age}}</h4>
现在要输出数据:
- {{with .user}}
- <h4>姓名:{{.Name}}h4>
- <h4>性别:{{.user.Gender}}h4>
- <h4>年龄:{{.Age}}h4>
- {{end}}
简单理解:相当于var .=.user
8、预定义函数 (了解)
执行模板时,函数从两个函数字典中查找:首先是模板函数字典,然后是全局函数字典。一般不在模板内定义函数,而是使用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,模板执行会中断并返回给调用模板执行者该错误;
- {{len .title}}
- {{index .hobby 2}}
9、自定义模板函数
- router.SetFuncMap(template.FuncMap{
- "formatDate": formatAsDate,
- })
- package main
-
- 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.SetFuncMap(template.FuncMap{
- "formatDate": formatAsDate,
- })
- //加载模板
- router.LoadHTMLGlob("templates/**/*")
-
- router.GET("/", func(c *gin.Context) {
- c.HTML(http.StatusOK, "default/index.html", map[string]interface{}{
- "title": "前台首页",
- "now": time.Now(),
- })
- })
- router.Run(":8080")
- }
模板里面的用法
- {{.now | formatDate}}
- 或者
- {{formatDate .now }}
5.4、嵌套template 1、新建templates/deafult/page_header.html {{ define “default/page_header.html” }}
2、外部引入 注意: 1、引入的名字为page_header.html中定义的名字 2、引入的时候注意最后的点(.) {{template “default/page_header.html” .}}