Ginopen in new window

Overview

1. 快速入门

  1. 初始化 golang 项目
$ cd <project-name>
$ go mod init <project-name>
$ go mod tidy
  1. 安装 gin 依赖包
$ go get -u "github.com/gin-gonic/gin"
  1. 创建 main.go 文件
$ vim main.go
package main

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

func main() {
	r := gin.Default()
	r.GET("/ping", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "pong",
		})
	})
	r.Run()	// curl http://localhost:8080/ping
}
  1. 运行项目并查看返回数据
$ go run main.go
$ curl "http://localhost:8080/ping"

2. 结构化项目

$ tree
.
├── `controller` <!-- 配置响应数据response -->
│   └── hello.go
├── `router` <!-- 配置路由url -->
│   └── router.go
├── go.mod
├── go.sum
└── `main.go` <!-- 配置post -->
package main

import (
	"fmt"
	"go-gin/router"
)

func main() {
	r := router.SetupRouter()
	r.Run(":8080")
}

package router

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

func SetupRouter() *gin.Engine {
	r := gin.Default()
	r.GET("/hello", controller.HelloController)

	return r
}
package controller

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

func HelloController(c *gin.Context) {
	c.JSON(200, gin.H{
		"message": "Welcome to Gin !",
	})
}
{"message": "Welcome to Gin !"}

3. Example

package main

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

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

	// GET无参数: curl http://localhost:8080/hello
	r.GET("/hello", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "Hello Gin !",
		})
	})

	// GET解析路径参数: /user/coulson
	r.GET("/user/:name", func(c *gin.Context) {
		name := c.Param("name")
		c.String(http.StatusOK, "Hello %s", name)
	})

	// GETQuery参数: users?name=coulson&role=developer,role可选
	r.GET("/users", func(c *gin.Context) {
		name := c.Query("name")
		role := c.DefaultQuery("role", "teacher")
		c.String(http.StatusOK, "%s is a %s", name, role)
	})

	r.GET("/api/v1/bar", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"success": true,
			"list":    []string{"home", "about", "docs", "footer"},
			"data":    "asas",
			"bar": []interface{}{
				&Bar{
					Title:   "条形图示例",
					Content: "直方图",
				},
			},
			"dateinfo": []interface{}{
				&DateInfo{
					dvp_1: "2022-04-09",
					dvp_2: "2022-04-09",
					dvp_3: "20220409",
				},
			},
			"msg":  "请求成功",
			"code": 1,
		})
	})

	r.POST("/post", func(c *gin.Context) {
		c.String(200, "这是post请求返回的数据")
	})

	// curl http://localhost:8080/form  -X POST -d 'username=geektutu&password=1234'
	r.POST("/form", func(c *gin.Context) {
		username := c.PostForm("username")
		password := c.DefaultPostForm("password", "000000") // 可设置默认值

		c.JSON(http.StatusOK, gin.H{
			"username": username,
			"password": password,
		})
	})

	// GET 和 POST 混合
	// curl "http://localhost:8080/posts?id=9876&page=7"  -X POST -d 'username=geektutu&password=1234'
	r.POST("/posts", func(c *gin.Context) {
		id := c.Query("id")
		page := c.DefaultQuery("page", "0")
		username := c.PostForm("username")
		password := c.DefaultPostForm("password", "000000") // 可设置默认值

		c.JSON(http.StatusOK, gin.H{
			"id":       id,
			"page":     page,
			"username": username,
			"password": password,
		})
	})

	// curl -g "http://localhost:8080/post?ids[Jack]=001&ids[Tom]=002" -X POST -d 'names[a]=Sam&names[b]=David'
	r.POST("/postmap", func(c *gin.Context) {
		ids := c.QueryMap("ids")
		names := c.PostFormMap("names")

		c.JSON(http.StatusOK, gin.H{
			"ids":   ids,
			"names": names,
		})
	})

	// group routes 分组路由
	defaultHandler := func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"path":    c.FullPath(),
			"success": "ture",
		})
	}

	// group: v1
	// http://localhost:8080/api/v1/task
	v1 := r.Group("/api/v1")
	{
		v1.GET("/task", defaultHandler)
		v1.GET("/series", defaultHandler)
	}
	// group: v2
	v2 := r.Group("/v2")
	{
		v2.GET("/task", defaultHandler)
		v2.GET("/series", defaultHandler)
	}

	r.Run() // listen and serve on 0.0.0.0:8080
	// r.Run(":8000")
}

type Bar struct {
	Title   string
	Content string
}

type DateInfo struct {
	dvp_1 string
	dvp_2 string
	dvp_3 string
}

总结

HTTP 方法

// 获取资源
r.GET()
// 新增
r.POST()
// 修改(覆盖)
r.PUT()
r.PATCH()	// 修改(局部)
// 删除
r.DELETE()

Body Response

/* ===== String ==== */
c.String(200, "hello world!")

/* ===== JSON ===== */
c.JSON(200, gin.H{
	"success": true,
	"code":    200,
	"message": "请求成功",
})
// c.PureJSON(200, gin.H{
// 	"html": "<b>Hello, world!</b>",
// })
// c.JSONP()
// c.AsciiJSON()
// c.SecureJSON()

/* ===== XML ===== */
c.XML()

/* ===== YML ===== */
c.YAML()



/* ===== HTML ==== */
c.HTML(200, gin.H {
	"html": "<b>Hello World !</b>"
})
c.HTML(200, "/html/index.tmpl", nil)

/* ===== File ==== */
file, err := c.FormFile("file")
c.SaveUploadedFile(file, file.Filename)

Body Request

// 自动绑定
// 如果是 `GET` 请求,只使用 `Form` 绑定引擎(`query`)。
// 如果是 `POST` 请求,首先检查 `content-type` 是否为 `JSON` 或 `XML`,然后再使用 `Form`(`form-data`)
c.ShouldBind()
// 绑定json
c.ShouldBindJSON()
c.ShouldBindXML()
c.ShouldBindYML()
// 显式绑定声明
c.ShouldBindWith(&obj, binding.Form)
c.ShouldBindWith(&obj, binding.Query)
c.ShouldBindBodyWith(&obj, binding.JSON)
c.ShouldBindBodyWith(&obj, binding.XML)
c.ShouldBindBodyWith(&obj, binding.YML)

Body Parmas

// curl http://localhost:8080/user/:name/*action
c.Param("name")
c.Param("action")

// query
// r.GET("/users")
// curl http://localhost:8080/users?id=12&page=2,page可选
c.Query("id")
c.DefaultQuery("page", "0")

// post form
// r.POST("/form")
// curl http://localhost:8080/form  -X POST -d 'username=geektutu&password=1234'
c.PostForm("name")
c.DefaultPostForm("password", "123456")

// map
// r.POST("/posts")
// curl -g "http://localhost:8080/post?ids[Jack]=001&ids[Tom]=002" -X POST -d 'names[a]=Sam&names[b]=David'
c.QueryMap("ids")
c.PostFormMap("names")
cookie, err := c.Cookie("gin_cookie")
if err != nil {
	cookie = "NotSet"
	c.SetCookie("gin_cookie", "test", 3600, "/", "localhost", false, true)
}
session := sessions.Default(c)
session.Get("hello")
session.Set("hello", "world")
session.Delete("tizi365")
session.Save()
// 删除整个session
// session.Clear()

Sample

HTTP 方法

r.GET("/someGet", getting)
r.POST("/somePost", posting)
r.PUT("/somePut", putting)
r.DELETE("/someDelete", deleting)
r.PATCH("/somePatch", patching)
r.HEAD("/someHead", head)
r.OPTIONS("/someOptions", options)

GET 无参方法

// curl http://localhost:8080/hello
r.GET("/hello", func(c *gin.Context) {
	c.JSON(200, gin.H{"message": "Hello Gin !"})
})

GET 路径解析参数

// curl http://localhost:8080/user/john
r.GET("/user/:name", func(c *gin.Context) {
	name := c.Param("name")
	c.String(http.StatusOK, "Hello %s", name)
})

GET Query 参数

// curl http://localhost:8080/users?name=coulson&role=developer
r.GET("/users", func(c *gin.Context) {
	name := c.Query("name")
	role := c.DefaultQuery("role", "teacher")
	c.String(http.StatusOK, "%s is a %s", name, role)
})

POST 无参方法

r.POST("/post", func(c *gin.Context) {
	c.String(200, "post方法")
})

POST Body-form 表单

// curl http://localhost:8080/form  -X POST -d 'username=geektutu&password=1234'
r.POST("/form", func(c *gin.Context) {
	username := c.PostForm("username")
	password := c.DefaultPostForm("password", "000000") // 可设置默认值

	c.JSON(http.StatusOK, gin.H{
		"username": username,
		"password": password,
	})
})

POST Query 参数与 From 表单

// curl "http://localhost:8080/posts?id=9876&page=7"  -X POST -d 'username=geektutu&password=1234'
r.POST("/posts", func(c *gin.Context) {
	id := c.Query("id")
	page := c.DefaultQuery("page", "0")
	username := c.PostForm("username")
	password := c.DefaultPostForm("password", "000000") // 可设置默认值

	c.JSON(http.StatusOK, gin.H{
		"id":       id,
		"page":     page,
		"username": username,
		"password": password,
	})
})

POST map

// curl -g "http://localhost:8080/post?ids[Jack]=001&ids[Tom]=002" -X POST -d 'names[a]=Sam&names[b]=David'
r.POST("/postmap", func(c *gin.Context) {
	ids := c.QueryMap("ids")
	names := c.PostFormMap("names")

	c.JSON(http.StatusOK, gin.H{"ids": ids,"names": names})
})

POST json 参数

r.POST("/json", ShowUser)

func ShowUser(c *gin.Context) {
	var user models.User
	if err := c.ShouldBindJSON(&user); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, user)
}

路由组

// group: v1
// http://localhost:8080/api/v1/task
v1 := r.Group("/api/v1")
{
	v1.GET("/task", defaultHandler)
	v1.GET("/series", defaultHandler)
}
// group: v2
v2 := r.Group("/api/v2")
{
	v2.GET("/task", defaultHandler)
	v2.GET("/series", defaultHandler)
}

路由参数

// 解析路径参数: "/user/:name" -> /user/john
c.Param("name")
// Query参数: users?name=coulson&role=developer,role可选
c.Query("name")
c.DefaultQuery("role", "teacher")

// post-form表单
c.PostForm("username")
c.DefaultPostForm("password", "000000")

// map: curl -g "http://localhost:8080/post?ids[Jack]=001&ids[Tom]=002" -X POST -d 'names[a]=Sam&names[b]=David'
c.QueryMap("ids")
c.PostFormMap("names")

// json
c.ShouldBindJSON(&user)

路由参数-路径参数 :name

// /user/john
r.GET("/user/:name", func(c *gin.Context) {
	name := c.Param("name")
	c.String(http.StatusOK, "Hello %s", name)
})

 
 


路由参数-Params ?name

// curl http://localhost:8080/user?name=tom&role=developer, role可选
router.GET("/user", func(c *gin.Context) {
	name := c.Query("name")
	role := c.DefaultQuery("role", "teacher")
	c.String(http.StatusOK, "%s is a %s", name, role)
})


 
 


路由参数-Body 参数

// curl http://localhost:8080/form  -X POST -d 'username=geektutu&password=1234'
r.POST("/form", func(c *gin.Context) {
	username := c.PostForm("username")
	password := c.DefaultPostForm("password", "000000") // 可设置默认值

	c.JSON(http.StatusOK, gin.H{
		"username": username,
		"password": password,
	})
})

路由绑定 uri

package main

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

type Person struct {
	ID   string `uri:"id" binding:"required,uuid"`
	Name string `uri:"name" binding:"required"`
}

func main() {
	route := gin.Default()
	route.GET("/:name/:id", func(c *gin.Context) {
		var person Person
		if err := c.ShouldBindUri(&person); err != nil {
			c.JSON(400, gin.H{"msg": err.Error()})
			return
		}
		c.JSON(200, gin.H{"name": person.Name, "uuid": person.ID})
	})
	route.Run(":8080")
}





 
 




 

 







模型绑定与验证

// 绑定 JSON
type Login struct {
	User     string `form:"user" json:"user" xml:"user"  binding:"required"`
	Password string `form:"password" json:"password" xml:"password" binding:"required"`
}

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

	// 绑定 JSON ({"user": "manu", "password": "123"})
	router.POST("/loginJSON", func(c *gin.Context) {
		var json Login
		if err := c.ShouldBindJSON(&json); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}

		if json.User != "manu" || json.Password != "123" {
			c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
			return
		}

		c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
	})

	// 绑定 XML (
	//	<?xml version="1.0" encoding="UTF-8"?>
	//	<root>
	//		<user>manu</user>
	//		<password>123</password>
	//	</root>)
	router.POST("/loginXML", func(c *gin.Context) {
		var xml Login
		if err := c.ShouldBindXML(&xml); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}

		if xml.User != "manu" || xml.Password != "123" {
			c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
			return
		}

		c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
	})

	// 绑定 HTML form表单 (user=manu&password=123)
	router.POST("/loginForm", func(c *gin.Context) {
		var form Login
		// 根据 Content-Type Header 推断使用哪个绑定器。
		if err := c.ShouldBind(&form); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}

		if form.User != "manu" || form.Password != "123" {
			c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
			return
		}

		c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
	})

	// 监听并在 0.0.0.0:8080 上启动服务
	router.Run(":8080")
}


 
 








 




















 
















 















渲染 String

c.String(200, "Hello world !")

渲染 JSON

r.GET("/json", func(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"success": true,
		"code":    200,
		"message": "请求成功",
	})
})

渲染 XML

r.GET("/xml", func(c *gin.Context) {
	c.XML(http.StatusOK, gin.H{
		"message": "hey",
		"status": 200
	})
})

渲染 YAML

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

记录日志

// 默认禁用控制台颜色(仅记录到文件时)
// gin.DisableConsoleColor()

// 记录到文件。
f, _ := os.Create("gin.log")
gin.DefaultWriter = io.MultiWriter(f)
// 强制日志颜色化(需要记录到控制台时)
gin.ForceConsoleColor()

// 记录到文件和控制台
f, _ := os.Create("gin.log")
gin.DefaultWriter = io.MultiWriter(f, os.Stdout)

curl

$ curl -v -X POST \
  http://localhost:8080/loginJSON \
  -H 'content-type: application/json' \
  -d '{ "user": "manu" }'

使用默认的中间件

// Default 使用 Logger 和 Recovery 中间件
r := gin.Default()

不使用默认的中间件

// 新建一个没有任何默认中间件的路由
r := gin.New()

// 全局中间件
// Logger 中间件将日志写入 gin.DefaultWriter,即使你将 GIN_MODE 设置为 release。
// By default gin.DefaultWriter = os.Stdout
r.Use(gin.Logger())

// Recovery 中间件会 recover 任何 panic。如果有 panic 的话,会写入 500。
r.Use(gin.Recovery())

FAQ

查看 go 环境配置

$ go env

安装 go get -u github.com/gin-gonic/gin 超时

$ go env -w GO111MODULE=on
$ go env -w GOPROXY=https://goproxy.cn,direct
$ go mod init [project-name]

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

热加载

修改接口文档后默认需重启项目,页面内容才能刷新;使用 fresh 后可实时刷新

# 安装fresh
$ go get -u github.com/pilu/fresh

$ cd <project-name>
$ fresh		# 运行fresh 代替 go run main.go

swag

# 安装swag (go-v1.17版本使用install)
$ go install github.com/swaggo/swag/cmd/swag
# 查看swag是否安装成功
$ swag -v
# 生成docs文档
$ swag init
# 启动项目, 并查看接口文档: http://localhost:8080/swagger/index.html
$ go run main.go
// main.go
package main

import (
	"github.com/gin-gonic/gin"
	swaggerFiles "github.com/swaggo/files"
	ginSwagger "github.com/swaggo/gin-swagger"
	_ "go-gin-swagger/docs"
)

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

	// http://localhost:8080/swagger/index.html
	r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
	r.GET("/hello", handleSwagger)

	r.Run()
}

// 测试接口 godoc
// @tags admin
// @router /hello [get]
// @summary 测试接口
// @description 测试swagger-hello接口
// @accept json
// @produce json
// @param name body string true "用户名,建议使用姓名拼音"
// @success 200 {json} {"success":true,"data":{},"msg":"ok"}
// @failure 500 {json} {"status":500,"data":{},"Msg":{},"Error":"error"}
func handleSwagger(c *gin.Context) {
	c.JSON(200, gin.H{
		"message": "Welcome to Gin !",
	})
}