推荐阅读官方文档 , 里面写的很详细
安装 gin
万物始于安装, 首先是安装 gin, 只需要执行下面一条指令即可:
go get -u github.com/gin-gonic/gin
如果你出现了报错的信息, 可以试试修改 go 的代理, 即下面这条指令:
go env -w GOPROXY=https://goproxy.cn,direct
在你的项目中导入 gin:
import "github.com/gin-gonic/gin"
执行以下代码, 在浏览器中输入 localhost:8090/ping
, 出现 pong
且后台不报错, 即证明安装成功
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
// 创建一个默认的路由引擎
r := gin.Default()
// 设置请求路径为 /ping, 请求方式为 get
r.GET("/ping", func(ctx *gin.Context) {
ctx.String(http.StatusOK, "pong")
})
// 启动HTTP服务,默认在8080端口启动服务, 这里指定为8090端口
r.Run(":8090")
}
返回数据
上述测试代码中的 ctx.String()
代表 gin 会给前端返回一个 string 类型的数据, 括号内的参数依次为 http 状态码和返回数据, gin 能返回的数据有很多, 但实际使用中应该是返回 json 的情况最多, 所以下面只介绍返回 json 的情况 (其他数据类型应该也大差不差)
-
可以将结构体直接作为 json 返回
type josnTest struct { Name string `json:"username"` Age int `json:"age"` } func main() { r := gin.Default() r.GET("/json", func(ctx *gin.Context) { test := josnTest{"测试", 1} ctx.JSON(http.StatusOK, test) }) r.Run(":8080") }
-
可以使用
gin.H()
返回type josnTest struct { Name string `json:"username"` Age int `json:"age"` } func main() { r := gin.Default() r.GET("/json", func(ctx *gin.Context) { ctx.JSON(http.StatusOK, gin.H{"username": "测试", "age": 1}) }) r.Run(":8080") }
获取参数
获取 QueryString 中的参数
queryString 是指 url 中 ?
后面类似于键值对的参数
请求路径为: http://localhost:8090/query?username=zhangsan&age=17
r.GET("/query", func(ctx *gin.Context) {
// Query 是从直接获取数据
username := ctx.Query("username")
// DefaultQuery 是如果没有获取到数据则会赋一个默认值
// 注意获取到的数据均为 string, 需要转换类型
age, _ := strconv.Atoi(ctx.DefaultQuery("age", "12"))
test := josnTest{username, age}
ctx.JSON(http.StatusOK, test)
})
获取表单中的数据
请求方式为 post, 请求数据通过 form 表单提交
r.POST("/form", func(ctx *gin.Context) {
// 与上一段代码类似
username := ctx.DefaultPostForm("username", "lisi")
age, _ := strconv.Atoi(ctx.PostForm("age"))
ctx.JSON(http.StatusOK, gin.H{"username": username, "age": age})
})
获取 json 数据
请求方式为 post, 请求数据通过 json 提交
r.POST("/json", func(ctx *gin.Context) {
// 先获取到原始的 json 数据
res, _ := ctx.GetRawData()
var t josnTest
// 再进行反序列化
json.Unmarshal(res, &t)
fmt.Println(t)
ctx.JSON(http.StatusOK, t)
})
获取路径中的数据
在 REST 风格
中, 我们常常会直接使用/[参数]
这种形式来发送请求
请求路径: http://localhost:8090/zhangsan/13
// 注意接口的写法
r.GET("/:username/:age", func(ctx *gin.Context) {
// 第一种方式: 获取对应名称的参数
username := ctx.Param("username")
age := ctx.Param("age")
// 第二种方式: 获取全部参数后再逐个获取
ps := ctx.Params
temp1 := ps.ByName("username")
temp2, _ := ps.Get("age")
fmt.Println(temp1, " ", temp2)
ctx.JSON(http.StatusOK, gin.H{"username": username, "age": age})
})
参数绑定
gin 提供了强大的参数绑定功能, 我们可以使用 .ShouldBind()
自动提取数据并绑定到对应的结构体上
结构体的定义为:
type bindingTest struct {
User string `form:"username" json:"username" binding:"required"`
Pwd string `form:"password" json:"password" binding:"required"`
}
绑定 QueryString 参数
r.GET("/binding/get", func(ctx *gin.Context) {
var test bindingTest
err := ctx.ShouldBind(&test)
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
} else {
fmt.Println(test.User, " ", test.Pwd)
ctx.JSON(http.StatusOK, test)
}
})
绑定 form 参数
r.POST("/binding/post/form", func(ctx *gin.Context) {
var test bindingTest
err := ctx.ShouldBind(&test)
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
} else {
fmt.Println(test.User, " ", test.Pwd)
ctx.JSON(http.StatusOK, test)
}
})
绑定 json 参数
r.POST("/binding/post/json", func(ctx *gin.Context) {
var test bindingTest
err := ctx.ShouldBind(&test)
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
} else {
fmt.Println(test.User, " ", test.Pwd)
ctx.JSON(http.StatusOK, test)
}
})
路由组
在实际应用中我们的接口很多情况下会有相同的前缀(如上一部分代码中每个路径前都有 /binding
), 所以 gin 中有一个路由组的概念, 通过设置路由组从而减少重复代码的书写
路由组的基本设置为:
r := gin.Default()
group := r.Group("/group")
// 这个括号可以不加
{
// 路径为 /group/test
group.GET("/test", )
}
重定向
http 重定向
注意: http 重定向时, 路径要加上 http://
前缀
r.GET("/get/baidu", func(ctx *gin.Context) {
ctx.Redirect(http.StatusMovedPermanently, "http://www.baidu.com")
// 这样是重定向至 /get/www.baidu.com
// ctx.Redirect(http.StatusMovedPermanently, "www.baidu.com")
})
路由重定向
r.GET("/get/ping", func(ctx *gin.Context) {
// 重定向至 /ping
ctx.Request.URL.Path = "/ping"
r.HandleContext(ctx)
})
r.GET("/ping", func(ctx *gin.Context) {
ctx.String(http.StatusOK, "pong")
})
程序清单
package main
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
type josnTest struct {
Name string `json:"username"`
Age int `json:"age"`
}
type bindingTest struct {
User string `form:"username" json:"username" binding:"required"`
Pwd string `form:"password" json:"password" binding:"required"`
}
func ginFunc() *gin.Engine {
r := gin.Default()
r.GET("/ping", func(ctx *gin.Context) {
ctx.String(http.StatusOK, "pong")
})
r.GET("/json", func(ctx *gin.Context) {
test := josnTest{"测试", 1}
fmt.Println(test)
ctx.JSON(http.StatusOK, test)
})
r.GET("/query", func(ctx *gin.Context) {
username := ctx.Query("username")
age, _ := strconv.Atoi(ctx.DefaultQuery("age", "12"))
test := josnTest{username, age}
ctx.JSON(http.StatusOK, test)
})
r.POST("/form", func(ctx *gin.Context) {
username := ctx.DefaultPostForm("username", "lisi")
age, _ := strconv.Atoi(ctx.PostForm("age"))
fmt.Print(age)
ctx.JSON(http.StatusOK, gin.H{"username": username, "age": age})
})
r.POST("/json", func(ctx *gin.Context) {
res, _ := ctx.GetRawData()
var t josnTest
json.Unmarshal(res, &t)
fmt.Println(t)
ctx.JSON(http.StatusOK, t)
})
r.GET("/:username/:age", func(ctx *gin.Context) {
username := ctx.Param("username")
age := ctx.Param("age")
// ps := ctx.Params
// temp1 := ps.ByName("username")
// temp2, _ := ps.Get("age")
// fmt.Println(temp1, " ", temp2)
ctx.JSON(http.StatusOK, gin.H{"username": username, "age": age})
})
return r
}
func ginBindingFunc() *gin.Engine {
r := gin.Default()
binding := r.Group("/binding")
{
binding.GET("/get", func(ctx *gin.Context) {
var test bindingTest
err := ctx.ShouldBind(&test)
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
} else {
fmt.Println(test.User, " ", test.Pwd)
ctx.JSON(http.StatusOK, test)
}
})
binding.POST("/post/form", func(ctx *gin.Context) {
var test bindingTest
err := ctx.ShouldBind(&test)
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
} else {
fmt.Println(test.User, " ", test.Pwd)
ctx.JSON(http.StatusOK, test)
}
})
binding.POST("/post/json", func(ctx *gin.Context) {
var test bindingTest
err := ctx.ShouldBind(&test)
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
} else {
fmt.Println(test.User, " ", test.Pwd)
ctx.JSON(http.StatusOK, test)
}
})
}
return r
}
func ginRedirect() *gin.Engine {
r := gin.Default()
r.GET("/get/baidu", func(ctx *gin.Context) {
ctx.Redirect(http.StatusMovedPermanently, "www.baidu.com")
})
r.GET("/get/ping", func(ctx *gin.Context) {
ctx.Request.URL.Path = "/ping"
r.HandleContext(ctx)
})
r.GET("/ping", func(ctx *gin.Context) {
ctx.String(http.StatusOK, "pong")
})
return r
}
func main() {
r := ginFunc()
// r := ginBindingFunc()
// r := ginRedirect()
r.Run(":8090")
}