Go开源宝藏Web框架 GIN 专场 (含思维导图) | 持续更新
Posted 小生凡一
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go开源宝藏Web框架 GIN 专场 (含思维导图) | 持续更新相关的知识,希望对你有一定的参考价值。
写在前面
本人只是一个Go语言的
初学者
,这篇总结只是把我平常经常用到的都总结起来而已。
具体详细的内容建议到去GIN的中文文档
查看。
当然这篇文章也会持续更新
,记录我的Web框架操练过程
这篇文章也会持续更新哒
思维导图
想获取
原图
或是.xmind
格式可在文末扫描并回复宝藏GIN
目录
github.com/gin-gonic/gin
1. 路由
1.1 基本路由
gin框架中
gin.Default()
创建路由实例- 给出了
gin.Context
,封装了request
和response
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
r := gin.Default() // 使用Default创建路由
r.GET("/", func(c *gin.Context) { // 这个c就是上下文,这个路由接收GET请求
c.String(http.StatusOK, "hello world") // 返回状态码和数据
})
_ = r.Run(":8000") //监听端口默认为8080
}
简单几行代码,就能实现一个非常简单的小网页了啦~
1.2 RESTful 路由
gin
支持RESTful
风格的API。RESTful
即Representational State Transfer的缩写。直接翻译的意思是表现层状态转化,是一种互联网应用程序的API设计理念:URL定位资源
,用HTTP
描述操作。- 是目前最主流的前后端交互规范形式。
r.POST("carts", api.CreateCart) // POST请求,一般提交表单
r.GET("carts/:id", api.ShowCarts) //GET请求,一般获取数据
r.PUT("carts", api.UpdateCart) //PUT请求,一般修改数据
r.DELETE("carts", api.DeleteCart) // DELETE请求,一般删除数据
1.3 参数接收
- 获取URL中的参数,一般用于
GET
,DELETE
请求。
Gin 中提供了.Param
进行参数的获取。
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
r := gin.Default()
r.GET("/user/:name/:action", func(c *gin.Context) {
name := c.Param("name") // 使用Param可以获取路由URL里面的数据
action := c.Param("action")
c.String(http.StatusOK, name+" rush "+action)
})
_ = r.Run(":8000")
}
- 获取表单参数,一般用于
POST
,PUT
请求。
Gin提供了.PostForm
方法获取Form表单的值。
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
r := gin.Default()
r.POST("/form", func(c *gin.Context) {
username := c.PostForm("username")
password := c.PostForm("password")
c.String(http.StatusOK, fmt.Sprintf("username:%s,password:%s", username, password))
})
_ = r.Run(":8000")
}
1.4 文件上传与下载
1.4.1 上传
- 提供了
.FormFile
来获取文件
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.POST("/upload", func(c *gin.Context) {
file, err := c.FormFile("file") // file是参数名称
if err != nil {
c.String(500, "上传图片出错") //出错就会返回这个
}
c.SaveUploadedFile(file, file.Filename) // 保存文件
c.String(http.StatusOK, file.Filename) // 返回状态码
})
_ = r.Run(":8000")
}
1.4.2 下载
1.5 路由拆分
我们一般来说,会把这个路由拆分成URL
和处理函数
。这样能高效管理我们的框架逻辑。
import (
"github.com/gin-gonic/gin"
"net/http"
)
func helloHandler(c *gin.Context) { // 把处理函数放在这里,可以处理一些复杂的业务
c.JSON(http.StatusOK, gin.H {
"message": "Hello World!", // 通过这个 gin.H{} 进行json格式的返回
})
}
// Router 配置路由信息
func Router() *gin.Engine {
r := gin.Default()
r.GET("/hello", helloHandler) // 这样这个路由就比较简洁了。
return r
}
2. 数据绑定
2.1 json数据格式
- 定义接收数据的结构体
binding:"required"
修饰的字段,是必须有的,没有是会报错的。后面可以跟着限制,满足限制
才不会报错。
type Product struct {
Name string `form:"name" json:"name"`
CategoryID int `form:"category_id" json:"category_id"`
Title string `form:"title" json:"title" binding:"required,min=2,max=100"`
Info string `form:"info" json:"info" binding:"max=1000"`
ImgPath string `form:"img_path" json:"img_path"`
Price string `form:"price" json:"price"`
DiscountPrice string `form:"discount_price" json:"discount_price"`
OnSale string `form:"on_sale" json:"on_sale"`
Num string `form:"num" json:"num"`
}
gin
框架中提供了一个ShouldBindJSON
方法,这样就能可以把json格式
解析到结构体
中。
func helloHandler(c *gin.Context) { // 把处理函数放在这里,可以处理一些复杂的业务
var data Product
if err := c.ShouldBindJSON(&data); err != nil {
// 进行数据的绑定,这样传过来的数据就会传入这个data当中
// gin.H封装了生成json数据的工具
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) // 返回错误信息
return
}
c.JSON(http.StatusOK, gin.H{"status": "200"})
}
// Router 配置路由信息
func Router() *gin.Engine {
r := gin.Default()
r.GET("/hello", helloHandler) // 这样这个路由就比较简洁了。
return r
}
2.2 form表单数据
同样的我们使用2.1
中的Product结构体
,注意一定要在结构体中,用tags
标志form
格式
上面的那个Product结构体是用tags标志了form格式和json格式的,所以两个都能用。
func helloHandler(c *gin.Context) { // 把处理函数放在这里,可以处理一些复杂的业务
var data Product
if err := c.Bind(&data); err != nil {
// Bind()默认解析并绑定form格式,这样传过来的数据就会传入这个data当中
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) // 返回错误信息
return
}
c.JSON(http.StatusOK, gin.H{"status": "200"})
}
2.3 URI
type Product struct {
Name string `form:"name" json:"name" uri:"name”`
CategoryID int `form:"category_id" json:"category_id" uri:"category_id"`
Title string `form:"title" json:"title" uri:“title binding:"required,min=2,max=100"`
Info string `form:"info" json:"info" uri:“info binding:"max=1000"`
ImgPath string `form:"img_path" json:"img_path" uri:"img_path"`
Price string `form:"price" json:"price" uri:“price`
DiscountPrice string `form:"discount_price" json:"discount_price" uri:"discount_price"`
OnSale string `form:"on_sale" json:"on_sale" uri:"on_sale"`
Num string `form:"num" json:"num" uri:"num"`
}
gin中提供了ShouldBindUri
方法进行绑定
func helloHandler(c *gin.Context) { // 把处理函数放在这里,可以处理一些复杂的业务
var data Product
if err := c.ShouldBindUri(&data); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) // 返回错误信息
return
}
c.JSON(http.StatusOK, gin.H{"status": "200"})
}
无论是
ShouldBindJSON
还是Bind
还是ShouldBindUri
不过我个人比较喜欢用c.ShouldBind()
这种,看个人喜欢吧~
3. 参数验证
3.1 普通情况
type User struct {
Phone string `form:"phone" binding:"required,gt=10"` //不能为空并且大于10
Name string `form:"name" binding:"required"`
Birthday time.Time `form:"birthday" time_format:"2006-01-02"`
// 满足格式才能接收,减少了if-else的判断
}
3.2 自定义情况
使用 gopkg.in/go-playground/validator.v8
包
- 在结构体上,绑定这个
NotNullAndAdmin
。
type User struct {
Phone string `form:"phone" binding:"required,gt=10"` //不能为空并且大于10
Name string `form:"name" binding:"NotNullAndAdmin"` // 自定义一个验证
Birthday time.Time `form:"birthday" time_format:"2006-01-02"`
// 满足格式才能接收,减少了if-else的判断
}
- 编写自定义方法
func nameNotNullAndAdmin(v *validator.Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
if value, ok := field.Interface().(string); ok {
return value != "" && !("admin" == value)// 字段不能为空,并且不等于 admin
}
return true
}
- 把我们的自定义注册到
validator
中就好了
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
_ = v.RegisterValidation("NotNullAndAdmin", nameNotNullAndAdmin) //注册定义的验证方法
}
4. 渲染
4.1 数据的响应
- Json 前后端主流的交互数据格式,我自己也是经常用这个。
func main() {
r := gin.Default()
r.GET("/json",JsonHandler)
_ = r.Run(":8000")
}
func JsonHandler(c *gin.Context) {
c.JSON(200, gin.H{"message": "HelloJson", "status": 200})
}
- Struct
func main() {
r := gin.Default()
r.GET("/struct", StructHandler)
_ = r.Run(":8000")
}
func StructHandler(c *gin.Context) {
var msg struct {
Name string
Message string
Number int
}
msg.Name = "FanOne"
msg.Message = "Golang"
msg.Number = 10001
c.JSON(200, msg)
}
- XML
func main() {
r := gin.Default()
r.GET("/xml", XMLHandler)
_ = r.Run(":8000")
}
func XMLHandler(c *gin.Context) {
c.XML(200, gin.H{"message": "HelloXml"})
}
- YAML
func main() {
r := gin.Default()
r.GET("/yaml", YAMLHandler)
_ = r.Run(":8000")
}
func YAMLHandler(c *gin.Context) {
c.YAML(200, gin.H{"message": "HelloYaml"})
}
- ProtoBuf 这几年很火的数据格式
func main() {
r := gin.Default()
r.GET("/protobuf", ProtoBufHandler)
_ = r.Run(":8000")
}
func ProtoBufHandler(c *gin.Context) {
reps := []int64{int64(1), int64(2)}
// 定义数据
label := "label"
// 传protobuf格式数据
data := &protoexample.Test{
Label: &label,
Reps: reps,
}
c.ProtoBuf(200, data)
}
4.2 模板渲染
- gin支持加载html模板, 然后根据模板参数进行配置并返回相应的数据,本质上就是字符串替换
LoadHTMLGlob()
方法可以加载模板文件Static
方法可以加载静态文件
- 加载HTML模板
func main() {
r := gin.Default()
r.LoadHTMLGlob("tempate/*") // 加载你的模板文件 就是html文件嗷
r.GET("/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "index.html", gin.H{"msg": "HelloWorld", "status": "200"})
})
r.Run()
}
- 加载Static静态文件
func main() {
r := gin.Default()
r.Static("/assets", "./assets") // 需要定义一个静态文件目录
r.GET("/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "index.html", gin.H{"msg": "HelloWorld", "status": "200"})
})
r.Run()
}
这两个其实很少用,因为基本都是前后端分离,所以就很少去用这些东西哒。
使用了这些意味着前后页面也在后端这边,高度耦合,所以不推荐使用嗷~
4.3 重定向
gin
包中有c.Redirect
可以使得跳转的时候重新跳到 baidu.com
这个页面
func main() {
r := gin.Default()
r.GET("/index", func(c *gin.Context) {
c.Redirect(http.StatusMovedPermanently, "http://www.baidu.com")
})
r.Run()
}
5. 中间件
这里会单独出一些中间件的介绍的,当然只是我自己经常用的到一些而已。
- CORS
- JWT-Go
- Logging
- …
6. 会话控制
6.1 Cookie
- HTTP是
无状态协议
,服务器不能记录浏览器的访问状态,也就是说服务器不能区分两次请求是否由同一个客户端发出。 - Cookie就是
解决HTTP协议无状态的方案
之一,中文是小甜饼的意思。 - Cookie实际上就是服务器保存在浏览器上的一段信息。浏览器有了Cookie之后,每次向服务器发送请求时都会同时将该信息发送给服务器,服务器收到请求后,就可以根据该信息处理请求。
- Cookie由服务器创建,并发送给浏览器,最终由
浏览器保存
。 - 服务端发送cookie给客户端,客户端请求时携带cookie
Cookie的问题有很多,比如说不安全
,明文
,增加带宽消耗
,cookie有上限
等等。
6.2 Sessions
Session是另一种记录客户状态的机制,不同的是Cookie保存在客户端浏览器
中,而Session保存在服务器
上。
客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器
上。这就是Session
。
客户端浏览器再次访问时只需要从该Session
中查找该客户的状态就可以了。
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
为自定义session后端提供cookie和文件系统session以及基础结构。
主要功能是:
- 简单的API:将其用作设置签名(以及可选的加密)cookie的简便方法。
- 内置的后端可将
session
存储在cookie
或文件系统中
。 - Flash消息:一直持续读取的
session值
。 - 切换session持久性(又称“记住我”)和设置其他属性的便捷方法。
旋转身份验证
和加密密钥
的机制。- 每个请求有多个session,即使是使用不同的后端也是如此。
- 自定义
session后端
的接口和基础结构
:可以使用通用API检索
并批量保存来源不同的session。
store := cookie.NewStore([]byte("something-very-secret")) // 存储
r.Use(sessions.Sessions("mysession", store)) // 其实这也算是一个中间件
7. 日志模块
Gin有提供自己默认的日志!
-
如果不指定的话,是把请求信息直接输出到控制台的。
-
把这个代码加到在初始化文件那里就行了。就不会输出在控制台了,而是会输出到.log文件当中
f, _ := os.Create("gin.log")
gin.DefaultWriter = io.MultiWriter(f)
// gin.DefaultWriter = io.MultiWriter(f, os.Stdout)
- 如果需要同时将日志写入文件和控制台,那就是这个代码
f, _ := os.Create("gin.log")
gin.DefaultWriter = io.MultiWriter(f, os.Stdout)
以上是关于Go开源宝藏Web框架 GIN 专场 (含思维导图) | 持续更新的主要内容,如果未能解决你的问题,请参考以下文章