这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天
Gin是一个golang的微框架,封装比较优雅,API友好,源码注释比较明确,具有快速灵活,容错方便等特点 gin框架是web框架 参考了这位大佬的文章——[gin框架](Gin框架介绍及使用 | 李文周的博客 (liwenzhou.com))
1、gin简介
插播MVC模型
MVC模型如下所示:
模型(Model):
数据库管理与设计。
控制器(Controller):
处理用户输入的信息,负责从视图读取数据,控制用户输入,并向模型发送数据源,是应用程序中处理用户交互的部分,负责管理与用户交互控制。
视图(View):
将信息显示给用户。
Gin框架的运行流程如下图所示。
安装
go get -u github.com/gin-gonic/gin
helloworld代码范例
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()
}
web框架所以少不了restful REST的含义就是客户端与Web服务器之间进行交互的时候,使用HTTP协议中的4个请求方法代表不同的动作。
GET用来获取资源POST用来新建资源PUT用来更新资源DELETE用来删除资源。
Gin框架支持开发RESTful API的开发。如:
-
1.获取文章 /blog/getXxx Get blog/Xxx
-
2.添加 /blog/addXxx POST blog/Xxx
-
3.修改 /blog/updateXxx PUT blog/Xxx
-
4.删除 /blog/delXxxx DELETE blog/Xxx
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",
})
})
}
这里面的context Context作为一个数据结构在中间件中传递本次请求的各种数据、管理流程,进行响应在请求来到服务器后,Context对象会生成用来串流程。
Context优点
我们可以 将Request的处理封装到Context中优点
func(context *gin.Context) {
context.String(http.StatusOK, "some post")
}
参数是gin.Context, 但是查看源码发现其实gin.Context在整个框架处理的地方只有下面这段
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
c := engine.pool.Get().(*Context)
c.writermem.reset(w)
c.Request = req
c.reset()
engine.handleHTTPRequest(c)
engine.pool.Put(c)
}
gin的context实现了的context.Context Interface.经过查看context.Context相关资料, Context的最佳运用场景就是对Http的处理.。封装成Conetxt另外的好处就是WithCancel, WithDeadline, WithTimeout, WithValue这些context包衍生的子Context就可以直接来使用.
简单来说就是封装起来可以调用一些API
gin渲染
gin框架可以对html 、静态文件、yml、JSON等。下面给出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 = "小王子"
msg.Message = "Hello world!"
msg.Age = 18
c.JSON(http.StatusOK, msg)
})
r.Run(":8080")
}
gin绑定参数和获取参数
querystring指的是URL中?后面携带的参数,例如:/user/search?username=小王子&address=沙河。 获取请求的querystring参数的方法如下:
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()
}
我们可以基于请求的Content-Type识别请求数据类型并利用反射机制自动提取请求中QueryString、form表单、JSON、XML等参数到结构体中。 下面的示例代码演示了.ShouldBind()强大的功能,它能够基于请求自动提取JSON、form表单和QueryString类型的数据,并把值绑定到指定的结构体对象。
// Binding from JSON
type Login struct {
User string `form:"user" json:"user" binding:"required"`
Password string `form:"password" json:"password" binding:"required"`
}
func main() {
router := gin.Default()
// 绑定JSON的示例 ({"user": "q1mi", "password": "123456"})
router.POST("/loginJSON", func(c *gin.Context) {
var login Login
if err := c.ShouldBind(&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示例 (/loginQuery?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")
}
gin还有中间件 路由等多种用途
例如中间件对HTTP Request请求进行拦截处理
// 自定义Go中间件 拦截器
func MyMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// Do something before request
// 通过自定义的中间件,设置的值,在后续处理只要调用了这个中间件,就可以获取到这个值
c.Set("usersession", "orzr3-123456")
c.Next() // 一定要调用该方法,否则后续的处理不会被执行
// c.Abort() // 一旦调用了该方法,后续的处理都不会被执行
// Do something after request
}
}
接收前端传递过来的参数,并且使用中间件
// 接收前端传递过来的参数,并且使用中间件
ginServer.GET("/user/info/handler", MyMiddleware(), func(context *gin.Context) {
// 获取中间件中设置的值
userSession := context.MustGet("usersession").(string)
// 打印
log.Println("userSession:================>", userSession)
userid := context.Query("userid")
username := context.Query("username")
context.JSON(http.StatusOK, gin.H{
"userid": userid,
"username": username,
"userSession": userSession,
})
})
不过注意的是
gin.Default()默认使用了Logger和Recovery中间件,其中:
Logger中间件将日志写入gin.DefaultWriter,即使配置了GIN_MODE=release。Recovery中间件会recover任何panic。如果有panic的话,会写入500响应码。
如果不想使用上面两个默认的中间件,可以使用gin.New()新建一个没有任何默认中间件的路由。