![d90c6f5d2d78f6123977decb38059a6c.png](https://i-blog.csdnimg.cn/blog_migrate/8d41a8ad315ee7bd36c98cd33059b366.jpeg)
Go语言基础(十八)
一、gin介绍
二、gin路由
三、gin数据解析与绑定
四、gin渲染
五、gin中间件
一、gin介绍
Go 语言最流行了两个轻量级 Web 框架分别是 Gin 和 Echo。gin是一个golang的微框架,封装比较优雅,API友好,源码注释明确。gin框架是Go语言进行web开发(api开发,微服务开发)框架中。
1、gin安装
go get -u -v github.com/gin-gonic/gin
2、hello world
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
// 1创建路由
r := gin.Default()
// 2绑定路由规则
r.GET("/", func(c *gin.Context) {
c.String(http.StatusOK, "hello world")
})
// 3监听端口,默认8000
r.Run(":8000")
}
在浏览器打开http://localhost:8000
![ee650c6c42859a193e169746b7301d55.png](https://i-blog.csdnimg.cn/blog_migrate/2bd7fb230265dc73398b22a950c1dc35.png)
二、gin路由
gin框架采用的路由是基于httprouter做的,gin支持Restful风格的API。
1、通过Context的Param方法获取API参数
func main() {
// 1创建路由,默认使用了两个中间件Logger(),Recovery()
//
r := gin.Default()
// 2绑定路由规则,
// gin.Context,封装了request和respose
r.GET("/welcome/:name/*action", func(c *gin.Context) {
name := c.Param("name")
action := c.Param("action")
c.String(http.StatusOK, name + "is" + action)
})
// 3监听端口,默认8000
r.Run(":8000")
}
2、通过DefaultQuery()或Query方法获取URL参数
r.GET("/user/:name/*action", func(c *gin.Context) {
name := c.DefaultQuery("name", "jack")
c.String(http.StatusOK, fmt.Sprintf("hello %s", name))
})
![91dd2094ffa9648436a43bb2d2988d19.png](https://i-blog.csdnimg.cn/blog_migrate/0ba2dc30ac58e6e7ad19a02c87435061.png)
3、通过PostForm()方法获取表单参数
首先创建一个html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<form action="http://127.0.0.1:8000/form" method="post" enctype="application/x-www-form-urlencoded">
用户名:<input type="text" name="username">
<br>
密  码:<input type="password" name="password">
兴  趣:
<input type="checkbox" value="run" name="hobby">跑步
<input type="checkbox" value="game" name="hobby">游戏
<input type="checkbox" value="money" name="hobby">金钱
<br>
<input type="submit" value="登录">
</form>
</body>
</html>
直接run起来,然后通过PostForm()方法获取表单参数:
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
)
func getting(c *gin.Context) {
}
func main() {
// 1创建路由,默认使用了两个中间件Logger(),Recovery()
//
r := gin.Default()
// 2绑定路由规则,
// gin.Context,封装了request和respose
r.POST("/form", func(c *gin.Context) {
typeStr := c.DefaultQuery("type", "alert")
username := c.PostForm("username")
password := c.PostForm("password")
// 多选框
hobbys := c.PostFormArray("hobby")
c.String(http.StatusOK, fmt.Sprintf("type is %s, username:%s, password:%s, hooby:%v",
typeStr, username, password, hobbys))
})
// 3监听端口,默认8000
r.Run(":8000")
}
![1fa1e40ddc065eb9e462dbc687aa6b4e.png](https://i-blog.csdnimg.cn/blog_migrate/9f159872740c1483b2173e0080db041a.png)
![f6093dec3025816c3333727cccb44bd7.png](https://i-blog.csdnimg.cn/blog_migrate/71fc88bc58f41c1779ab659fcd3276e3.png)
4、上传文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<form action="http://127.0.0.1:8000/upload" method="post" enctype="multipart/form-data">
头像:
<input type="file" name="file">
<br>
<input type="submit" value="提交">
</form>
</body>
</html>
在浏览器中打开,然后运行:
package main
import (
"github.com/gin-gonic/gin"
"log"
"fmt"
)
func main() {
// 1创建路由,默认使用了两个中间件Logger(),Recovery()
//
r := gin.Default()
// 2绑定路由规则,
// gin.Context,封装了request和respose
r.POST("/upload", func(c *gin.Context) {
file, _ := c.FormFile("file")
log.Println(file.Filename)
c.SaveUploadedFile(file, file.Filename)
c.String(200, fmt.Sprintf("%s upload file!", file.Filename))
})
// 3监听端口,默认8000
r.Run(":8000")
}
![e68c9a9d8cfae81f0f109990c793322e.png](https://i-blog.csdnimg.cn/blog_migrate/191785930d725aee8885b833ab46f814.png)
![835624d2f4598f83cb84f01bd42ad56c.png](https://i-blog.csdnimg.cn/blog_migrate/05a0d0ca19e47417387f1dc6a6238bc6.png)
5、routes group
routes group就是为了管理一些相同的URL。
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func main() {
// 1创建路由
r := gin.Default()
// 路由组1,处理get请求
v1 := r.Group("/v1")
{
v1.GET("/login", login)
v1.GET("submit", submit)
}
v2 := r.Group("/v2")
{
v2.POST("/login", login)
v2.POST("submit", submit)
}
r.Run(":8000")
}
func login(c *gin.Context) {
name := c.DefaultQuery("name", "jack")
c.String(200, fmt.Sprintf("hello %sn", name))
}
func submit(c *gin.Context) {
name := c.DefaultQuery("name", "tom")
c.String(200, fmt.Sprintf("hello %sn", name))
}
![6c2c7918904994b440b0e4e8dee82d3b.png](https://i-blog.csdnimg.cn/blog_migrate/b01ba3dec437299bffef98313c566d91.jpeg)
6、路由原理
httproter会将所有的路由规则构造一颗前缀树。
三、gin数据解析和绑定
1、json数据解析和绑定
客户端传参,后端接收并解析得到结构体。
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
// 定义接收数据的结构体
type Login struct {
// binding:"required"修饰的字段,若接收为空则报错,是必须字段。
User string `form:"username" json:"user" uri:"user" xml:"user" binging:"requierd"`
Passward string `form:"password" json:"password" uri:"password" xml:"password" binging:"requierd"`
}
func main() {
r := gin.Default()
// json绑定
r.POST("loginJson", func(c *gin.Context) {
// 声明接收变量
var json Login
// 将request中body数据,自动按照json格式解析到结构体
if err := c.ShouldBindJSON(&json); err != nil {
// 返回错误信息
// gin.H封装了生成json数据的工具
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// 判断用户名密码是否正确
if json.User != "root" || json.Passward != "admin" {
c.JSON(http.StatusBadRequest, gin.H{"status": "304"})
return
}
c.JSON(http.StatusBadRequest, gin.H{"status": "200"})
})
r.Run(":8000")
}
![02e653b7e895ff2d2f1a745edc2396e2.png](https://i-blog.csdnimg.cn/blog_migrate/87f2621c830cf1a09b786b9fe72d2b65.png)
2、表单数据绑定与解析
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<form action="http://127.0.0.1:8000/loginForm" method="post" enctype="multipart/form-data">
用户名:<input type="text" name="username">
<br>
密  码:<input type="password", name="password">
<input type="submit", value="登录">
</form>
</body>
</html>
然后执行:
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
// 定义接收数据的结构体
type Login struct {
// binding:"required"修饰的字段,若接收为空则报错,是必须字段。
User string `form:"username" json:"user" uri:"user" xml:"user" binging:"requierd"`
Passward string `form:"password" json:"password" uri:"password" xml:"password" binging:"requierd"`
}
func main() {
r := gin.Default()
// json绑定
r.POST("loginForm", func(c *gin.Context) {
// 声明接收变量
var form Login
// Bind默认解析并绑定form格式
// 根据请求头中content-type自动推断
if err := c.Bind(&form); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// 判断用户名密码是否正确
if form.User != "root" || form.Passward != "admin" {
c.JSON(http.StatusBadRequest, gin.H{"status": "304"})
return
}
c.JSON(http.StatusBadRequest, gin.H{"status": "200"})
})
r.Run(":8000")
}
![4b131b3a81a3c20e217caa9e11df2fbf.png](https://i-blog.csdnimg.cn/blog_migrate/e90cec1dd27101cebae51d87628e5c7f.png)
3、URL数据绑定与解析
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
// 定义接收数据的结构体
type Login struct {
// binding:"required"修饰的字段,若接收为空则报错,是必须字段。
User string `form:"username" json:"user" uri:"user" xml:"user" binging:"requierd"`
Passward string `form:"password" json:"password" uri:"password" xml:"password" binging:"requierd"`
}
func main() {
r := gin.Default()
// json绑定
r.GET("/:user/:password", func(c *gin.Context) {
// 声明接收变量
var login Login
// Bind默认解析并绑定form格式
// 根据请求头中content-type自动推断
if err := c.ShouldBindUri(&login); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// 判断用户名密码是否正确
if login.User != "root" || login.Passward != "admin" {
c.JSON(http.StatusBadRequest, gin.H{"status": "304"})
return
}
c.JSON(http.StatusBadRequest, gin.H{"status": "200"})
})
r.Run(":8000")
}
![d32d6eca5c519cf77c6eccea513a70c2.png](https://i-blog.csdnimg.cn/blog_migrate/8a851feade7c2979928d8dc145f0f9dc.png)
四、gin渲染
1、各种数据格式的响应
package main
import (
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/testdata/protoexample"
)
// 多种响应方式
func main() {
r := gin.Default()
// 1、json
r.GET("/someJson", func(c *gin.Context) {
c.JSON(200, gin.H{"message":"someJson", "status":200})
})
// 2、结构体响应
r.GET("/someStruct", func(c *gin.Context) {
var msg struct {
Name string
Message string
Memner int
}
msg.Name = "root"
msg.Message = "message"
msg.Memner = 123
c.JSON(200, msg)
})
// 3、xml
r.GET("/someXml", func(c *gin.Context) {
c.XML(200, gin.H{"message":"abc"})
})
// 4、YAML
r.GET("/someYaml", func(c *gin.Context) {
c.YAML(200, gin.H{"name":"zhangsan"})
})
// 5、protobuf格式,谷歌开发的高效存储读取的工具
r.GET("/protobuf", func(c *gin.Context) {
reps := []int64{int64(1), int64(2)}
// 定义数据
label := "label"
// 传protobuf数据
data := &protoexample.Test{
Label: &label,
Reps: reps,
}
c.ProtoBuf(200, data)
})
r.Run(":8000")
}
![0c09bb752c45be5c528919dc39e1bdd5.png](https://i-blog.csdnimg.cn/blog_migrate/b908c7dd5ec223ec845a2572e853de08.png)
![832a70a64e835baef10d43be19342454.png](https://i-blog.csdnimg.cn/blog_migrate/99eace3289ef7d8a0eed245d2b279524.png)
![820a9e4776388eb9177bc6c3813b2b0f.png](https://i-blog.csdnimg.cn/blog_migrate/9397b1473945a01ad190cade18df77e4.png)
![b45d5391eb89666226c2a99f6a38e669.png](https://i-blog.csdnimg.cn/blog_migrate/6a62d4700f9956e152ba626e129a7a8d.png)
![fdb0c680c300d2bfad16fccfe19a3fa2.png](https://i-blog.csdnimg.cn/blog_migrate/f2d96de1a1be5d992e06b90edca8a954.png)
![98672b73073d7ff2554db518a06d7ce1.png](https://i-blog.csdnimg.cn/blog_migrate/9e795cf536b04912aeea3dccbc5a5c9d.png)
2、HTML模板渲染
gin支持加载HTML模板,然后根据响应的模板参数进行配置并返回响应的数据,本质上就是字符串替换。LoadHTMLGlob()方法可以加载模板文件。
index.tmpl
<html>
<h1>
{{.title}}
</h1>
</html>
使用HTML模板进行渲染:
package main
import "github.com/gin-gonic/gin"
// HTML渲染
func main() {
r := gin.Default()
// 加载模板文件
r.LoadHTMLGlob("templates/*")
// 或者
// r.LoadHTMLFiles("templates/index.tmpl")
r.GET("/index", func(c *gin.Context) {
// 根据文件名渲染
// 最终json将title替换
c.HTML(200, "index.tmpl", gin.H{"title":"我的标题"})
})
r.Run(":8000")
}
![bf5b7197d614f195a27f307c6672bd7a.png](https://i-blog.csdnimg.cn/blog_migrate/d8f02252bd876a38de38bafbd90b9222.png)
3、重定向
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
r := gin.Default()
r.GET("/redirect", func(c *gin.Context) {
// 支持内部和外部重定向
c.Redirect(http.StatusMovedPermanently, "http://www.baidu.com/")
})
r.Run(":8000")
}
4、同步异步
goroutine可以很方便地实现异步处理,另外,在启动goroutine时,不应该使用原始上下文,必须使用它的只读副本。
package main
import (
"github.com/gin-gonic/gin"
"log"
"time"
)
func main() {
r := gin.Default()
r.GET("/long_async", func(c *gin.Context) {
// 需要一个副本
copyContext := c.Copy()
// 实现异步处理
go func() {
time.Sleep(3 * time.Second)
log.Printf("异步执行:" + copyContext.Request.URL.Path)
}()
})
// 同步
r.GET("/long_sync", func(c *gin.Context) {
time.Sleep(3 * time.Second)
log.Printf("同步执行:" + c.Request.URL.Path)
})
r.Run(":8000")
}
五、gin中间件
- gin可以构建中间件,但是它只对注册过的路由函数起作用
- 对于分组路由,嵌套使用中间件
- 中间件分为全局中间件、单个路由中间件和群组中间件
- gin中间件必须是一个gin HandlerFunc类型
1、全局中间件
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"time"
)
// 定义中间件
func MiddleWare() gin.HandlerFunc {
return func(c *gin.Context) {
t := time.Now()
fmt.Println("中间件开始执行了")
// 设置变量到Context的key中,可以通过Get()获取
c.Set("request", "中间件")
// 执行中间件
c.Next()
status := c.Writer.Status()
fmt.Println("中间件执行完毕", status)
t2 := time.Since(t)
fmt.Println("time", t2)
}
}
func main() {
// 1创建路由
r := gin.Default()
// 2注册中间件
r.Use(MiddleWare())
{
// 同步
r.GET("/middleware", func(c *gin.Context) {
// 取值
req, _ := c.Get("request")
fmt.Println("request", req)
// 页面接收
c.JSON(200, gin.H{"request":req})
})
}
r.Run(":8000")
}
![da9fe7fe32c4a043a8a9fa6a47be72ec.png](https://i-blog.csdnimg.cn/blog_migrate/e35a19c612a8197370536f436b46bfbd.png)
![0101920e22b9d3bcefb5f14bb2f926ad.png](https://i-blog.csdnimg.cn/blog_migrate/359688f73ef6baf0398144c299aaf270.jpeg)
2、局部中间件
// 局部中间件
r.GET("/middleware2", MiddleWare(), func(c *gin.Context) {
// 取值
req, _ := c.Get("request")
fmt.Println("request", req)
// 页面接收
c.JSON(200, gin.H{"request":req})
})
3、中间件小练习
定义程序计时中间件,然后定义2个路由,执行函数后打印统计时间。
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"time"
)
func myTime(c *gin.Context) {
start := time.Now()
c.Next()
// 统计时间
end := time.Since(start)
fmt.Println("程序用时:", end)
}
func main() {
// 1创建路由
r := gin.Default()
// 2注册中间件
r.Use(myTime)
shoppingGroup := r.Group("/shopping")
{
shoppingGroup.GET("/index", shopIndexHandler)
shoppingGroup.GET("/home", shopHomeHandler)
}
r.Run(":8000")
}
func shopIndexHandler(c *gin.Context) {
time.Sleep(time.Second * 5)
}
func shopHomeHandler(c *gin.Context) {
time.Sleep(time.Second * 3)
}
![bbf1a84cb6afdbda4905ce94db8e59bb.png](https://i-blog.csdnimg.cn/blog_migrate/a5f4368c8d7047433e09e3d85b50b888.png)
我是尾巴~
每日一句毒鸡汤:有时候就是这样,嘴上说了不可能,但心里却早已想好了千万种可能!
本次推荐:PDF全文翻译网站
Free Online Document Translator - Preserves your document's layout (Word, PDF, Excel, Powerpoint, OpenOffice, text)www.onlinedoctranslator.com继续加油~!