Golang Web框架Gin入门详解

2,341 阅读6分钟

!!! 本文已参与「新人创作礼」活动,一起开启掘金创作之路。更多干货文章,可以访问 菜鸟厚非

一、简介

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 继续测试,这里不一一 列举。如下 在这里插入图片描述

在这里插入图片描述

在这里插入图片描述