Gin-Vue:www.gin-vue-admin.com/guide/serve…
文档参考: www.kancloud.cn/shuangdeyu/…
1、一个简单的实例
package main
import "github.com/gin-gonic/gin"
func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
r.Run() // 端口默认8080 配置格式r.Run(":2022") 远程ip访问"0.0.0.0:2022"
}
2、创建路由
// 使用默认中间件创建一个gin路由器
// 携带基础中间件启动 logger and recovery (crash-free) 中间件
router := gin.Default()
// 使用无中间件默认启动
r := gin.New()
3、获取接口参数
- GET 参数在url中 url进行传参
- c.Param("key")
- c.Query("key")
- c.DefaultQuery("key", "defaultVal")
// /path/foo?user=gogo&pwd=123456
r.GET("/path/:id", func(c *gin.Context) {
id := c.Param("id")
user := c.Query("user")
pwd := c.Query("pwd")
c.JSON(200, gin.H{
"id": id,
"user": user,
"pwd": pwd,
})
})
- POST 参数在form body中 或者url中
- c.DefaultPostForm("key", "defaultVal")
- c.PostForm("key")
r.POST("/path", func(c *gin.Context) {
user := c.DefaultPostForm("user", "gogo")
pwd := c.PostForm("pwd")
c.JSON(200, gin.H{
"user": user,
"pwd": pwd,
})
})
-
DELETE 一般情况为url 也可以用body
-
PUT 参数在form body或者url中
4、使用bind绑定参数和参数验证
Gin使用 go-playground/validator.v8 验证参数,查看完整文档。
需要在绑定的字段上设置tag,比如,绑定格式为json,需要这样设置 json:"fieldname"
Bind模式获取参数和表单验证
bind默认如何使用
bind模式一定要设置tag json form url
MustBind(弃用)
不推荐使用,对返回code不好控制
ShouldBind
表单验证 自定义验证
使用tag进行参数绑定器设置
// 1、简单实例 使用ShouldBindJSON()和参数结构体字段tag配置json:参数名 进行绑定
type PostParams struct {
Name string `json:"name"`
Age int `json:"age"`
Sex bool `json:"sex"`
}
func main() {
r := gin.Default()
r.POST("/testBind", func(c *gin.Context) {
var p PostParams
err := c.ShouldBindJSON(&p)
if err != nil {
c.JSON(200, gin.H{
"msg": "接口报错",
"data": gin.H{},
})
} else {
c.JSON(200, gin.H{
"msg": "接口成功",
"data": p,
})
}
})
r.Run(":2000")
}
// 2、简单实例 使用ShouldBinduri()和参数结构体字段tag配置uri:参数名 进行绑定
type PostParams struct {
Name string `uri:"name"`
Age int `uri:"age"`
Sex bool `uri:"sex"`
}
func main() {
r := gin.Default()
r.POST("/testBind/:name/:age/:sex", func(c *gin.Context) {
var p PostParams
err := c.ShouldBindUri(&p)
if err != nil {
c.JSON(200, gin.H{
"msg": "接口报错",
"data": gin.H{},
})
} else {
c.JSON(200, gin.H{
"msg": "接口成功",
"data": p,
})
}
})
r.Run(":2000")
}
// 3、简单实例 使用ShouldBinduri()和参数结构体字段tag配置uri:参数名 进行绑定
type PostParams struct {
Name string `form:"name"`
Age int `form:"age"`
Sex bool `form:"sex"`
}
func main() {
r := gin.Default()
r.POST("/testBind", func(c *gin.Context) {
var p PostParams
err := c.ShouldBindQuery(&p)
if err != nil {
c.JSON(200, gin.H{
"msg": "接口报错",
"data": gin.H{},
})
} else {
c.JSON(200, gin.H{
"msg": "接口成功",
"data": p,
})
}
})
r.Run(":2000")
}
// 4、简单实例 使用ShouldBindQuery()和参数结构体字段tag配置form:参数名 进行绑定
type PostParams struct {
Name string `form:"name"`
Age int `form:"age"`
Sex bool `form:"sex"`
}
func main() {
r := gin.Default()
r.POST("/testBind", func(c *gin.Context) {
var p PostParams
err := c.ShouldBindQuery(&p)
if err != nil {
c.JSON(200, gin.H{
"msg": "接口报错",
"data": gin.H{},
})
} else {
c.JSON(200, gin.H{
"msg": "接口成功",
"data": p,
})
}
})
r.Run(":2000")
}
// 思考
// 如果是Get,name接收不到请求中的Post的数据??
// 如果是Post,首先判断`content-type`的类型是json或者是xml,然后使用对应的绑定器获取数据
ShouldBind()表单验证
必填项配置: binding:"required"
type PostParams struct {
Name string `json:"name" binding:"required"`
Age int `json:"age" binding:"required"`
Sex bool `json:"sex" binding:"required"`
}
自定义配置器
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"github.com/go-playground/validator/v10"
)
// 1、自定义校验器 需要binding:"required,函数名"
type PostParams struct {
Name string `json:"name"`
Age int `json:"age" binding:"required,mustBig"`
Sex bool `json:"sex"`
}
// 2、配置校验器函数
func mustBig(fl validator.FieldLevel) bool {
if fl.Field().Interface().(int) <= 18 {
return false
} else {
return true
}
}
func main() {
r := gin.Default()
// 3、注册校验器
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
v.RegisterValidation("mustBig", mustBig)
}
r.POST("/testBind", func(c *gin.Context) {
var p PostParams
err := c.ShouldBindJSON(&p)
if err != nil {
fmt.Println(err.Error())
c.JSON(200, gin.H{
"msg": "接口报错",
"data": gin.H{},
})
} else {
c.JSON(200, gin.H{
"msg": "接口成功",
"data": p,
})
}
})
r.Run(":2000")
}
5、上传文件
参考文档:www.kancloud.cn/shuangdeyu/…
PostMan接口:http://127.0.0.1:2000/upload Header:{"Content-Type":"multipart/form-data"} Body:选文件类型{"file":"点击按钮选择文件"}
package main
import (
"io"
"os"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.POST("/upload", func(c *gin.Context) {
// 实现二: 实现c.SaveUploadedFile(file, "./"+file.Filename)
file, _ := c.FormFile("file")
in, _ := file.Open()
defer in.Close()
out, _ := os.Create("./" + file.Filename)
io.Copy(out, in)
//实现一: c.SaveUploadedFile(file, "./"+file.Filename)
c.JSON(200, gin.H{
"msg": file,
})
})
r.Run(":2000")
}
// 文件上传保存并且再传给前端的实现
r.POST("/upload", func(c *gin.Context) {
// 实现c.SaveUploadedFile(file, "./"+file.Filename)
file, _ := c.FormFile("file")
in, _ := file.Open()
defer in.Close()
out, _ := os.Create("./" + file.Filename)
io.Copy(out, in)
c.Writer.Header().Add("Content-Disposition", fmt.Sprint("attachment; filename=%s", file.Filename))
c.File("./" + file.Filename)
})
r.Run(":2000")
多文件上传
r.POST("/upload", func(c *gin.Context) {
// 实现c.SaveUploadedFile(file, "./"+file.Filename)
form, _ := c.MultipartForm()
file, _ := form.File["file"]
for _, file := range file {
log.Println(file.Filename)
}
})
r.Run(":2000")
// 终端打印
[GIN-debug] POST /upload --> main.main.func1 (3 handlers)
[GIN-debug] Listening and serving HTTP on :2000
2023/04/18 22:44:52 2023-04-18_22-39.png
2023/04/18 22:44:52 计划方案.doc
[GIN] 2023/04/18 - 22:44:52 | 200 | 585.946µs | 127.0.0.1 | POST "/upload"
6、 中间件和路由分组
路由
路由分组:对router创建Group就是分组,同一个分组会拥有同一前缀和同一中间件 好处:路由结构更加清晰、更加方便管理路由
v1 := r.Group("v1")
v1.GET("test", func(c *gin.Context) {
fmt.Println("我在分组方法内部")
c.JSON(200, gin.H{
"success": true,
})
})
r.Run(":2000")
// http://127.0.0.1:2000/v1/test
// 返回:{ "success": true }
中间件
Q:什么是中间件? A:在请求到达路由的方法的前和后进行的一系列操作
Q:如何使用中间件 A:在路由器(路由组)上进行user操作 后面传入中间件函数即可 * 中间件操作函数: c.Next() c.Abort() c.Set() c.Get() blog.csdn.net/qq_37767455…
r := gin.Default()
// 使用gin.Default()底层会给我们默认加上两个中间件
// 源码
// Default returns an Engine instance with the Logger and Recovery middleware already attached.
func Default() *Engine {
debugPrintWARNINGDefault()
engine := New()
engine.Use(Logger(), Recovery())
return engine
}
自定义中间件创建
// 一个中间件
func middle() gin.HandlerFunc {
return func(c *gin.Context) {
//c.Next() 是否通过
fmt.Println("我在方法前......")
c.Next()
fmt.Println("我在方法后......")
}
}
func main() {
r := gin.Default()
v1 := r.Group("v1").Use(middle())
v1.GET("test", func(c *gin.Context) {
fmt.Println("我在分组方法内部")
c.JSON(200, gin.H{
"success": true,
})
})
r.Run(":2000")
}
// 终端打印
我在方法前......
我在分组方法内部
我在方法后......
// 两个中间件
func middle() gin.HandlerFunc {
return func(c *gin.Context) {
//c.Next() 是否通过
fmt.Println("我在方法前......")
c.Next()
fmt.Println("我在方法后......")
}
}
func middleTwo() gin.HandlerFunc {
return func(c *gin.Context) {
//c.Next() 是否通过
fmt.Println("我在方法前 Two......")
c.Next()
fmt.Println("我在方法后 Two......")
}
}
func main() {
r := gin.Default()
// 两种写法都可以
// v1 := r.Group("v1").Use(middle(), middleTwo())
v1 := r.Group("v1").Use(middle()).Use(middleTwo())
v1.GET("test", func(c *gin.Context) {
fmt.Println("我在分组方法内部")
c.JSON(200, gin.H{
"success": true,
})
})
r.Run(":2000")
}
// 终端
我在方法前......
我在方法前 Two......
我在分组方法内部
我在方法后 Two......
我在方法后......
具体使用可以参考: gin-vue-admin的middleware/jwt.go和casbin_rcba.go【鉴权】
7、日志
-
为什么使用日志? 记录参数信息、猜测用户行为、复现系统Bug并修复
-
in自带日志写入中间件:自定义比较麻烦 第三方日志中工具:go-logging、logrus
-
日志切割:自行根据时间在写入时间判断进行切割日志、借助成品的日志包:go-file-rotatelogs、file-rotatelogs
文档参考: gin自带的日志格式实例【不推荐使用,灵活度不够】 www.kancloud.cn/shuangdeyu/… www.kancloud.cn/shuangdeyu/…
实例参考:gin-vue-admin日志模块
8、GORM
9、参考文档
learnku.com/docs/gorm/v… gorm.io/zh_CN/docs/
www.kancloud.cn/shuangdeyu/… learnku.com/docs/the-wa… golang-china.github.io/gopl-zh/ gin-gonic.com/zh-cn/docs/ gorm.io/zh_CN/ juejin.cn/post/716923… github.com/uber-go/gui… github.com/fatih/vim-g…
juejin.cn/post/684490… coolshell.cn/articles/21…
入门
web框架
Gorm框架
GORM - The fantastic ORM library for Golang, aims to be developer friendly.
编码规范
vim-IDE(使用vs-code忽略)