目录
💟这里是CS大白话专场,让枯燥的学习变得有趣!
💟没有对象不要怕,我们new一个出来,每天对ta说不尽情话!
💟好记性不如烂键盘,自己总结不如收藏别人!
🧡Gin简介
💌Gin 是一个 golang 的微框架,封装比较优雅,API 友好,源码注释比较明确,已经发布了1.0版本。具有快速灵活,容错方便等特点。其实对于 golang 而言,web 框架的依赖要远比 Python,Java 之类的要小。自身的 net/http 足够简单,性能也非常不错。框架更像是一些常用函数或者工具的集合。借助框架开发,不仅可以省去很多常用的封装带来的时间,也有助于团队的编码风格和形成规范。
🍠Gin 框架是开源的,可以在 github 上下载其源码库,查看相应的说明:Gin源码库地址
🍠Gin 框架有一个官方网站,有对 Gin 相关的介绍和学习资料:Gin官网
🧡Gin特性
🍠速度:Gin 之所以被很多企业和团队所采用,第一个原因是因为其速度快,性能表现出众。
🍠中间件:和 iris 类似,gin 在处理请求时,支持中间件操作,方便编码处理。
🍠路由:在 gin 中可以非常简单地实现路由,路由组解析功能。
🍠内置渲染:Gin 支持 JSON、XML 和 HTML 等多种数据格式的渲染,并提供方便的操作 API。
🧡Gin环境搭建
💌gin 框架需要 go 语言版本在1.6及以上。可以通过 go version 查看 go 语言版本是否符合要求。
通过go get命令安装gin框架:
go get -u github.com/gin-gonic/gin
💌安装完毕后,可以在当前系统的 $GOPATH 目录下的 src/github.com 目录中找到 gin-gonic 目录,该目录下存放的就是 gin 框架的源码。
🧡HTTP请求和参数解析
创建Engine
💌在 gin 框架中,Engine 被定义成为一个结构体,其中包含了路由组、中间件、页面渲染接口、框架配置设置等相关内容。两种创建方式如下所示:
engine1 = gin.Default()
engine2 = gin.New()
💌gin.Default() 内也调用 gin.New() 创建Engine实例,但会默认使用Logger和Recovery中间件。
🍠Logger 是负责进行打印并输出日志的中间件,方便开发者进行程序调试。
🍠Recovery 中间件的作用是如果程序执行过程中遇到 panic 中断了服务,Recovery 会恢复程序执行,并返回服务器500内部错误。
💌通常情况下,使用默认的 gin.Default() 创建 Engine 实例。
Handle方法处理
💌在 Engine 中可以使用 Handle 方法处理 HTTP 请求。Handle 方法包含三个参数,具体如下:
func (group *RouterGroup) Handle(httpMethod, relativePath string, handlers ...HandlerFunc)
🍠httpMethod:表示要处理的 HTTP 的请求类型,是 GET、POST、DELETE、OPTIONS、HEAD、PUT、TRACE、CONNECT 请求类型中的一种。
🍠relativePath:表示要解析的接口,由开发者进行定义。
🍠handlers:是处理对应的请求的代码的定义。
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func main() {
engine := gin.Default()
//GET
//http://localhost:8080/hello?name=Jodie
engine.Handle("GET", "/hello", func(context *gin.Context) {
path := context.FullPath()
fmt.Println(path)
//获取name值,若没有则返回stranger
name := context.DefaultQuery("name", "stranger")
fmt.Println(name)
//页面显示内容
context.Writer.Write([]byte("Hello, " + name))
})
//POST
//http://localhost:8080/login
engine.Handle("POST", "/login", func(context *gin.Context) {
fmt.Println(context.FullPath())
username := context.PostForm("username")
password := context.PostForm("password")
fmt.Println(username)
fmt.Println(password)
context.Writer.Write([]byte("Hello, " + username))
})
engine.Run()
}
Engine分类处理
💌除了 Engine 中包含的通用的处理方法以外,Engine 还可以按类型直接解析。Engine 中包含有get方法、post方法、delet方法等与 HTTP 请求类型对应的方法。
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func main() {
engine := gin.Default()
//GET
engine.GET("/hello", func(context *gin.Context) {
path := context.FullPath()
fmt.Println(path)
name := context.Query("name")
fmt.Println(name)
context.Writer.Write([]byte("Hello, " + name))
})
//POST
engine.POST("/login", func(context *gin.Context) {
fmt.Println(context.FullPath())
username, existName := context.GetPostForm("username")
if existName {
fmt.Println(username)
}
password, existWord := context.GetPostForm("password")
if existWord {
fmt.Println(password)
}
context.Writer.Write([]byte("Hello, " + username))
})
//DELETE
engine.DELETE("/user/:id", func(context *gin.Context) {
userID := context.Param("id")
fmt.Println(userID)
context.Writer.Write([]byte("DELETE " + userID))
})
engine.Run()
}
表单实体绑定
💌使用 PostForm 这种单个获取属性和字段的方式,代码量较多,需要一个一个属性进行获取。而表单数据的提交,往往对应着完整的数据结构体定义,其中对应着表单的输入项。gin 框架提供了数据结构体和表单提交数据绑定的功能,提高表单数据获取的效率。如下所示:
以用户注册功能为例,假设用户注册提交表单数据包含三项:username、phone 和 password。
type UserRegister struct {
Username string `form:"username" binding:"required"`
Phone string `form:"phone" binding:"required"`
Password string `form:"password" binding:"required"`
}
创建了 UserRegister 结构体用于接收表单数据,通过 tag 标签的方式设置每个字段对应的 form 表单中的属性名,通过 binding 设置属性是否必须。
ShouldBindQuery
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"log"
)
func main() {
engine := gin.Default()
//GET
//http://localhost:8080/hello?username=Jodie&password=1234
engine.GET("/hello", func(context *gin.Context) {
fmt.Println(context.FullPath())
var user User
err := context.ShouldBindQuery(&user)
if err != nil {
log.Fatal(err.Error())
return
}
fmt.Println(user.Username)
fmt.Println(user.Password)
context.Writer.Write([]byte("Hello, " + user.Username))
})
engine.Run()
}
type User struct {
Username string `form:"username" binding:"required"`
Password string `form:"password" binding:"required"`
}
ShouldBind
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"log"
)
func main() {
engine := gin.Default()
//POST
engine.POST("/regist", func(context *gin.Context) {
fmt.Println(context.FullPath())
var register UserRegister
err := context.ShouldBind(®ister)
if err != nil {
log.Fatal(err.Error())
return
}
fmt.Println(register.Username)
fmt.Println(register.Phone)
context.Writer.Write([]byte("Hello, " + register.Username))
})
engine.Run()
}
type UserRegister struct {
Username string `form:"username" binding:"required"`
Phone string `form:"phone" binding:"required"`
Password string `form:"password" binding:"required"`
}
JSON
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"log"
)
func main() {
engine := gin.Default()
//POST
engine.POST("/addUser", func(context *gin.Context) {
fmt.Println(context.FullPath())
var person Person
context.BindJSON(&person)
err := context.BindJSON(&person)
if err != nil {
log.Fatal(err.Error())
return
}
fmt.Println(person.Username)
context.Writer.Write([]byte("Add " + person.Username))
})
engine.Run()
}
type Person struct {
Username string `form:"username" binding:"required"`
Phone string `form:"phone" binding:"required"`
Password string `form:"password" binding:"required"`
}
💌JSON格式提交请求:
{
"username":"Nike"
"phone":"13265496230"
"password":"1234"
}
多数据格式返回请求结果
[]byte切片
💌通过 context.Writer.Write([]byte(返回结果)) 实现,前文内容已经使用,Writer 是 gin 框架中封装的一个 ResponseWriter 接口类型。
string
💌ResponseWriter 还封装了 WriteString 方法返回数据,通过 context.Writer.WriteString(返回结果) 实现。
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func main() {
engine := gin.Default()
//byte[]
engine.GET("/hellobyte", func(context *gin.Context) {
path := "请求路径:" + context.FullPath()
fmt.Println(path)
context.Writer.Write([]byte(path))
})
//string
engine.GET("/hellostring", func(context *gin.Context) {
path := "请求路径:" + context.FullPath()
fmt.Println(path)
context.Writer.WriteString(path)
})
engine.Run()
}
JSON
💌JSON 格式规范使用的更为普遍,context 包含的 JSON 方法可以将结构体类型的数据转换成 JSON 格式的结构化数据,然后返回给客户端。
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func main() {
engine := gin.Default()
engine.GET("/hellojson", func(context *gin.Context) {
path := "请求路径:" + context.FullPath()
fmt.Println(path)
//200:正常 404:NOT FOUND 500:内部错误
//map类型
context.JSON(200, map[string]interface{}{
"code": 1,
"message": "OK",
"data": path,
})
})
//struct类型
engine.GET("/hellostruct", func(context *gin.Context) {
path := "请求路径:" + context.FullPath()
fmt.Println(path)
resp := Response{Code: 1, Message: "OK", Data: path}
context.JSON(200, &resp)
})
engine.Run()
}
type Response struct {
Code int
Message string
Data interface{}
}
HTML
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
engine := gin.Default()
//设置html目录
engine.LoadHTMLGlob("./html/*")
//静态文件目录
engine.Static("/img", "./img")
engine.GET("/hellohtml", func(context *gin.Context) {
path := "请求路径:" + context.FullPath()
fmt.Println(path)
context.HTML(http.StatusOK, "index.html", gin.H{
"path": path,
"title": "Hello!",
})
})
engine.Run()
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{.title}}</title>
</head>
<body>
{{.path}}
<div><img src="../img/Miku.jpeg"></div>
</body>
</html>
路由分组
💌路由组是 router.Group 中的一个方法,用于对请求进行分组,相同模块的功能接口,可以使用路由组进行分类处理。
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func main() {
engine := gin.Default()
routerGroup := engine.Group("/user")
routerGroup.POST("/register", registerHandle)
routerGroup.DELETE("/:id", deleteHandle)
engine.Run()
}
func registerHandle(context *gin.Context) {
path := "用户注册:" + context.FullPath()
fmt.Println(path)
context.Writer.WriteString(path)
}
func deleteHandle(context *gin.Context) {
path := "用户删除:" + context.FullPath()
userID := context.Param("id")
fmt.Println(path + " " + userID)
context.Writer.WriteString(path + " " + userID)
}
🧡中间件的使用
💌在实际的业务开发和处理中,会有更复杂的业务和需求场景。一个完整的系统可能要包含鉴权认证、权限管理、安全检查、日志记录等多维度的系统支持,对于系统中的所有业务都适用,可以将一些通用业务抽离出来单独进行开发,然后以插件化的形式进行对接。这种方式既保证了系统功能的完整,同时又有效地将具体业务和系统功能进行解耦,还可以达到灵活配置的目的。
💌自定义中间件有两条标准:
🍠类型为 func 函数
🍠返回值类型为 HandlerFunc
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func main() {
engine := gin.Default()
//engine.Use(RequestInfos()) //所有接口均可使用中间件
engine.GET("/middle", func(context *gin.Context) {
context.JSON(200, map[string]interface{}{
"code": 1,
"msg": context.FullPath(),
})
})
//只有指定接口可以使用
engine.GET("/only", RequestInfos(), func(context *gin.Context) {
fmt.Println("中断操作")
context.JSON(200, map[string]interface{}{
"code": 2,
"msg": context.FullPath(),
})
})
engine.Run()
}
// 打印请求信息的中间件
func RequestInfos() gin.HandlerFunc {
return func(context *gin.Context) {
path := context.FullPath()
method := context.Request.Method
fmt.Println("请求路径:", path)
fmt.Println("请求method:", method)
context.Next() //中断操作,先执行主函数内容
fmt.Println("状态码:", context.Writer.Status())
}
}