!!! 本文已参与「新人创作礼」活动,一起开启掘金创作之路。更多干货文章,可以访问 菜鸟厚非
一、简介
Gin 是一个 Golang 的微框架,封装比较优雅,API 友好,源码注释比较明确,具有快速灵活,容错方便等特点。对于 Golang 而言,web框架的依赖要远比 Python,Java 之类的要小。自身的 net/http 足够简单,性能也非常不错。
二、环境
假设 Golang 的开发环境等配置是好的,不会的同学可以参照,下面两篇文件:
三、安装 Gin
Golang 工作空间下,在项目目录,执行下面命令即可安装 gin,我这里项目名为 golang_gin。如下:
go get -u github.com/gin-gonic/gin
四、HelloWorld
下面代码,定义了一个 /hello 的 url,访问后返回 Hello world!
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 字符串
五、RESTful Demo
这里展示 gin 中 get、post、put、delete 四种方法,假设有个如下需求:
| 请求方法 | URL | 含义 |
|---|---|---|
| GET | /book | 查询书籍信息 |
| POST | /book | 创建书籍记录 |
| PUT | /book | 更新书籍信息 |
| DELETE | /book | 删除书籍信息 |
下面代码定义 /book ,get、post、put、delete 四种访问方法
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/book", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "GET",
})
})
r.POST("/book", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "POST",
})
})
r.PUT("/book", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "PUT",
})
})
r.DELETE("/book", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "DELETE",
})
})
r.Run()
}
将上面的代码保存并编译执行,然后使用 API 测试工具,分别以 get、post、put、delete 访问 /bool。如下
在控制台,也可以看到访问 /book 的情况。如下:
六、JSON 渲染
JSON 渲染,API response 返回 JSON 格式的数据。如下代码
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
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 = "小王子"
msg.Message = "Hello world!"
msg.Age = 18
c.JSON(http.StatusOK, msg)
})
r.Run(":8080")
}
运行代码,然后访问 /someJSON、/moreJSON 即可看到对应的 JSON 格式的响应。如下:
七、XML 渲染
XML 渲染,API response 返回 XML 格式的数据。如下代码
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
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")
}
运行代码,然后访问 /someXML、/moreXML 即可看到对应的 XML 格式的响应。如下:
八、YMAL 渲染
YMAL 渲染,API response 返回 YMAL 格式的数据。YMAL 不通于 JSOM XML ,YMAL 会下载成一个文件。如下代码
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/someYAML", func(c *gin.Context) {
c.YAML(http.StatusOK, gin.H{"message": "ok", "status": http.StatusOK})
})
r.Run()
}
运行代码,访问 someYAML ,浏览器会直接下载一个文件,记事本打开即可。如下
八、获取 QueryString
QueryString 指的是URL中 ? 后面携带的参数,例如:/user/search?username=小王子&address=上海,查询 QueryString 就是查询 ? 后面参数的值。如下代码
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
//Default返回一个默认的路由引擎
r := gin.Default()
r.GET("/user/search", func(c *gin.Context) {
username := c.DefaultQuery("username", "小王子")
//username := c.Query("username")
address := c.Query("address")
//输出json结果给调用方
c.JSON(http.StatusOK, gin.H{
"message": "ok",
"username": username,
"address": address,
})
})
r.Run()
}
运行代码,然后访问 /user/search?username=小王子&address=上海 。如下:
九、获取 Form 参数
查询 Form 参数,是指请求的数据通过 Form 表单来提交,例如向 /user/search 发送一个POST请求,获取请求数据的方式如下:
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
//Default返回一个默认的路由引擎
r := gin.Default()
r.POST("/user/search", func(c *gin.Context) {
// DefaultPostForm取不到值时会返回指定的默认值
//username := c.DefaultPostForm("username", "小王子")
username := c.PostForm("username")
address := c.PostForm("address")
//输出json结果给调用方
c.JSON(http.StatusOK, gin.H{
"message": "ok",
"username": username,
"address": address,
})
})
r.Run(":8080")
}
运行代码,使用 API 测试工具 ,访问 /user/search ,body 设置为 Form 格式。如下:
十、获取 Path 参数
获取 Path 参数,请求的参数通过 URL 路径传递,获取其中某段的值,例如:/user/search/小王子/上海,代码如下
func main() {
//Default返回一个默认的路由引擎
r := gin.Default()
r.GET("/user/search/:username/:address", func(c *gin.Context) {
username := c.Param("username")
address := c.Param("address")
//输出json结果给调用方
c.JSON(http.StatusOK, gin.H{
"message": "ok",
"username": username,
"address": address,
})
})
r.Run(":8080")
}
运行代码,然后访问 /user/search/小王子/上海 。如下:
十一、参数绑定
参数绑定,是为了能够更方便的获取请求相关参数,提高开发效率,我们可以基于请求的 content-type 识别请求数据类型并利用反射机制自动提取请求中 querystring、form 表单、JSON、XML 等参数到结构体中。代码如下
package main
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
// 绑定JSON的示例 ({"user": "q1mi", "password": "123456"})
router.POST("/loginJSON", func(c *gin.Context) {
var login Login
if err := c.ShouldBindJSON(&login); err == nil {
fmt.Printf("login info:%#v\n", login)
c.JSON(http.StatusOK, gin.H{
"user": login.User,
"password": login.Password,
})
} else {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
})
// 绑定form表单示例 (user=q1mi&password=123456)
router.POST("/loginForm", func(c *gin.Context) {
var login Login
// ShouldBind()会根据请求的Content-Type自行选择绑定器
if err := c.ShouldBind(&login); err == nil {
c.JSON(http.StatusOK, gin.H{
"user": login.User,
"password": login.Password,
})
} else {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
})
// 绑定querystring示例 (user=q1mi&password=123456)
router.GET("/loginForm", func(c *gin.Context) {
var login Login
// ShouldBind()会根据请求的Content-Type自行选择绑定器
if err := c.ShouldBind(&login); err == nil {
c.JSON(http.StatusOK, gin.H{
"user": login.User,
"password": login.Password,
})
} else {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
})
// Listen and serve on 0.0.0.0:8080
router.Run(":8080")
}
// Binding from JSON
type Login struct {
User string `form:"user" json:"user" binding:"required"`
Password string `form:"password" json:"password" binding:"required"`
}
运行代码,使用 API 测试工具 ,分别访问对应的 url,数据的格式并进行对应的设置。如下:
十二、重定向
12.1 HTTP 重定向
HTTP 重定向很容易。 内部、外部重定向均支持。如下:
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/test", func(c *gin.Context) {
c.Redirect(http.StatusMovedPermanently, "http://www.google.com/")
})
// Listen and serve on 0.0.0.0:8080
r.Run(":8080")
}
运行代码,浏览器访问 /test ,会跳转到 google 搜索主页
12.2 路由重定向
路由重定向,是指当浏览器发起一个请求url 为 test,在后端可以让 test 重定向到 /test2 的功能。如下代码:
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/test", func(c *gin.Context) {
// 指定重定向的URL
c.Request.URL.Path = "/test2"
r.HandleContext(c)
})
r.GET("/test2", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"hello": "world"})
})
// Listen and serve on 0.0.0.0:8080
r.Run(":8080")
}
运行下面,访问 /test,可以看到,响应为 /test2 定义的 response。如下:
控制台,也可以看出,虽然访问了 /test ,但也出现 /test2
十三、路由
Gin 的路由分为普通路由与路由组,接下来进行详细介绍
13.1 普通路由
普通路由,即是 restful 的几种,Gin 中有个 Any ,可以匹配 restful 任何方法
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/index", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "index",
})
})
r.POST("/login", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "login",
})
})
r.Any("/any", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "any",
})
})
// 为没有配置处理函数的路由添加处理程序。默认情况下它返回404代码
r.NoRoute(func(c *gin.Context) {
c.JSON(http.StatusNotFound, "404")
})
// Listen and serve on 0.0.0.0:8080
r.Run(":8080")
}
运行代码,分文不同 的 url 继续测试,这里不一一 列举。如下
13.2 路由组
路由组,是指可以进行组合的路由,有点像嵌套路由。如下:
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
userGroup := r.Group("/user")
{
userGroup.GET("/index", func(c *gin.Context) { c.JSON(http.StatusOK, "/user/index") })
userGroup.GET("/login", func(c *gin.Context) { c.JSON(http.StatusOK, "/user/login") })
userGroup.POST("/login", func(c *gin.Context) { c.JSON(http.StatusOK, "/user/login") })
}
shopGroup := r.Group("/shop")
{
shopGroup.GET("/index", func(c *gin.Context) { c.JSON(http.StatusOK, "/shop/index") })
shopGroup.GET("/cart", func(c *gin.Context) { c.JSON(http.StatusOK, "/shop/cart") })
shopGroup.POST("/checkout", func(c *gin.Context) { c.JSON(http.StatusOK, "/shop/checkout") })
}
r.Run()
}
运行代码,分文不同 的 url 继续测试,这里不一一 列举。如下