一 、gin 入门

1. 安装gin :下载并安装 gin包:

$ go get -u github.com/gin-gonic/gin

2. 将 gin 引入到代码中:

import "github.com/gin-gonic/gin"

3.初始化项目

go mod init gin

4.完整代码

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()
}

5.启动项目

go run main.go  # 运行 main.go 并且在浏览器中访问 0.0.0.0:8080/ping

6.效果图




gin框架如何删除session_Powered by 金山文档


gin框架如何删除session_gin_02


二、基础知识

  1. go get -u

参数介绍:

  • -d 只下载不安装
  • -f 只有在你包含了 -u 参数的时候才有效,不让 -u 去验证 import 中的每一个都已经获取了,这对于本地 fork 的包特别有用
  • -fix 在获取源码之后先运行 fix,然后再去做其他的事情
  • -t 同时也下载需要为运行测试所需要的包
  • -u 强制使用网络去更新包和它的依赖包
  • -v 显示执行的命

三、gin测试用例

  1. AsciiJSON 生成具有转义的非 ASCII 字符的 ASCII-only JSON。
package main

import (
    "github.com/gin-gonic/gin"
    "net/http"
)

func main()  {
    r:=gin.Default()

    r.GET("/someJson", func(c *gin.Context) {
        data:=map[string]interface{}{
            "lang":"go 语言",
            "tag":"<br>",
        }
        c.AsciiJSON(http.StatusOK,data)
    })
    r.Run(":8081")
}


gin框架如何删除session_Powered by 金山文档_03


gin框架如何删除session_gin_04


gin框架如何删除session_Powered by 金山文档_05


为什么 浏览器和 终端命令行curl打印出来的结果格式不一样?gin.AsciiJSON

答:编码问题

  1. 绑定HTML复选框
package main

import "github.com/gin-gonic/gin"

type myForm struct {
    Colors []string `form:"colors[]"`
}

func formHandler(c *gin.Context) {
    var fakeForm myForm
    c.ShouldBind(&fakeForm)
    c.JSON(200, gin.H{"color": fakeForm.Colors})
}

func innerHandler(c *gin.Context) {
    c.HTML(200, "form.html", nil)
}

func main() {
    r := gin.Default()
    r.LoadHTMLGlob("views/*")
    r.GET("/", innerHandler)
    r.POST("/", formHandler)

    r.Run(":8082")
}

views/form.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/" method="POST">
    <p>Check some colors</p>
    <label for="red">Red</label>
    <input type="checkbox" name="colors[]" value="red" id="red">
    <label for="green">Green</label>
    <input type="checkbox" name="colors[]" value="green" id="green">
    <label for="blue">Blue</label>
    <input type="checkbox" name="colors[]" value="blue" id="blue">
    <input type="submit">
</form>
</body>
</html>


gin框架如何删除session_Powered by 金山文档_06


gin框架如何删除session_Powered by 金山文档_07


  1. 绑定查询字符串或表单数据
package main

import (
    "github.com/gin-gonic/gin"
    "log"
    "time"
)
//表单字符串
type PersonString struct {
    Name     string    `form:"name"`
    Address  string    `form:"address" `
    Birthday time.Time `form:"birthday" time_format:"2006-01-02" time_utc:"1"`
}
 //请求json格式
type PersonJson struct {
    Name     string    `json:"name"`
    Address  string    `json:"address"`
    Birthday time.Time `json:"birthday" time_format:"2006-01-02" time_utc:"1"`
}

func startPage(c *gin.Context) {
    var person PersonString
    err := c.Bind(&person)
    //ShouldBind 与 Bind 打印结果一样
    //err:=c.ShouldBind(&person)
    if err == nil {
        log.Println(person.Name)
        log.Println(person.Address)
        log.Println(person.Birthday)
        log.Println("Binding success...")
    } else {
        log.Println("Binding failed...")
        log.Println(err)
    }

    var personJson PersonJson
    errJson := c.BindJSON(&personJson)
    if errJson == nil {
        log.Println(personJson.Name)
        log.Println(personJson.Address)
        //log.Println(personJson.Birthday)
        log.Println("BindJson success...")
    } else {
        log.Println("BindJson failed...")
        log.Println(errJson)
    }

    c.String(200, "success")
}
func main() {
    log.Println("Hello World")
    route := gin.Default()
    route.GET("/testing", startPage)
    route.Run(":8083")
}

请求:

string:

curl -X GET "http://0.0.0.0:8083/testing?name=appleboy&address=xyz&birthday=2023-03-15"
打印:结果
success


gin框架如何删除session_gin_08


josn:

curl -X GET "http://0.0.0.0:8083/testing" --data '{"name":"JJ", "address":"xyz"}' -H "Content-Type:application/json"

结果:success


gin框架如何删除session_Powered by 金山文档_09


效果图:


gin框架如何删除session_gin_10


  1. 绑定uri
package main

import "github.com/gin-gonic/gin"

type Person struct {
    ID   string `uri:"id" building:"required,uuid"`
    Name string `uri:"name" building:"required"`
}

func main() {
    r := gin.Default()
    r.GET("/:name/:id", func(c *gin.Context) {
        var person Person
        if err := c.ShouldBindUri(&person); err != nil {
            c.JSON(400, gin.H{"msg": err})
            return
        }
        c.JSON(200, gin.H{"name": person.Name, "uuid": person.ID})
    })

    r.Run(":8080")

}


gin框架如何删除session_gin_11


func main() {
    r := gin.Default()
    r.GET("/:name/:id", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "name": c.Param("name"),
            "id":   c.Param("id"),
        })
    })
    r.Run()
}


gin框架如何删除session_gin框架如何删除session_12


  1. 控制log 高亮输出,默认开启高亮
//强制log 高亮
gin.ForceConsoleColor()

//关闭log高亮
//gin.DisableConsoleColor()


gin框架如何删除session_gin框架如何删除session_13


6.自定义http配置:(监听端口,读写时间,最大读写字节数)

func main() {
    r := gin.Default()
    r.GET("/ping", func(c *gin.Context) {
        c.String(200, "pong")
    })

    //两行作用一样,谁先执行谁生效
    //r.Run(":8080")
    //http.ListenAndServe(":8081",r)
    s := &http.Server{
        Addr:           ":8084",
        Handler:        r, //实例句柄
        ReadTimeout:    10 * time.Second,
        WriteTimeout:   10 * time.Second,
        MaxHeaderBytes: 1 << 20,
    }
    s.ListenAndServe()
}
  1. 自定义log文件
func main() {
    r := gin.New()

    r.Use(gin.LoggerWithFormatter(func(params gin.LogFormatterParams) string {
        return fmt.Sprintf("%s - [%s] \"%s %s %s %d %s \"%s\" %s\"\n",
            params.ClientIP,
            params.TimeStamp.Format(time.RFC3339),
            params.Method,
            params.Path,
            params.Request.Proto,
            params.StatusCode,
            params.Latency,
            params.Request.UserAgent(),
            params.ErrorMessage,
        )
    }))

    r.Use(gin.Recovery())
    r.GET("/ping", func(c *gin.Context) {
        c.String(200, "pong")
    })
    r.Run(":8080")
}


gin框架如何删除session_gin框架如何删除session_14


  1. 自定义中间件
//自定义中间件
func logger() gin.HandlerFunc {
    return func(c *gin.Context) {
        t := time.Now()
        //设置变量
        c.Set("lxw", "this is string")
        c.Set("lxwInt", 123456789)

        //请求前
        c.Next()

        //请求后
        latency := time.Since(t)
        log.Println(latency) //花费时间

        //获取发送的code
        statusCode := c.Writer.Status()
        log.Println(statusCode)
    }
}
func main() {
    r := gin.New()
    r.Use(logger()) //引用(相当于include)

    r.GET("/test", func(c *gin.Context) {
        /*** MustGet returns the value for the given key if it exists, otherwise it panics.
        返回给定键的值(如果存在),否则会死机*/
        varInt := c.MustGet("lxwInt").(int) //int 是对变量lxwInt 的限制要求,若不是int则报错
        varString := c.GetString("lxw")
        log.Println(varInt,varString)
    })
    r.Run(":8080")
}


gin框架如何删除session_github_15


  1. 自定义验证器
//自定义验证器
//输入和输出日期必填;输出日期大于输入日期;输入和输出日期符合自定的验证器(日期需在今天日期之后)

type Booking struct {
    CheckIn  time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`
    CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn bookabledate" time_format:"2006-01-02"`
}

//验证器
var bookableDate validator.Func = func(fl validator.FieldLevel) bool {
    date, ok := fl.Field().Interface().(time.Time)
    if ok {
        today := time.Now()
        if today.After(date) {
            return false
        }
    }
    return true
}

func getBookable(c *gin.Context) {
    var b Booking
    if err := c.ShouldBindWith(&b, binding.Query); err == nil {
        c.JSON(http.StatusOK, gin.H{"message": "book data is validate"})
    } else {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
    }
}

func main() {
    r := gin.Default()
    if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
        v.RegisterValidation("bookabledate", bookableDate)
    }
    r.GET("/bookable", getBookable)
    r.Run(":8080")
}


gin框架如何删除session_gin_16


请求地址:输入和输出日期必填;输出日期大于输入日期;输入和输出日期符合自定的验证器(日期需在今天日期之后)

http://0.0.0.0:8080/bookable?check_in=2023-02-08&check_out=2023-03-09

  1. 定义路由日志格式
//定义路由日志格式
func main() {
    r := gin.Default()

    gin.DebugPrintRouteFunc = func(httpMethod, absolutePath, handlerName string, nuHandlers int) {
        log.Printf("endpoint %v %v %v %v\n", httpMethod, absolutePath, handlerName, nuHandlers)
    }
    r.POST("/aaa", func(c *gin.Context) {
        c.JSON(http.StatusOK, "aaa")
    })
    r.GET("/bbb", func(c *gin.Context) {
        c.JSON(http.StatusOK, "bbb")
    })
    r.GET("/ccc", func(c *gin.Context) {
        c.JSON(http.StatusOK, "ccc")
    })
    r.Run()
}


gin框架如何删除session_Powered by 金山文档_17


  1. 优雅的关机或重启
//优雅的关机或重启
func main() {
    r := gin.Default()
    r.GET("/", func(c *gin.Context) {
        time.Sleep(5 * time.Second)
        c.String(http.StatusOK, "welcome server")
    })

    srv := &http.Server{
        Addr:    ":8080",
        Handler: r,
    }

    go func() {
        //服务连接:打印链接错误信息
        if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
            log.Fatalf("listen: %s \n", err)
        }
    }()

    //等待中断信号优雅关闭服务器(设置5秒的超时时间)
    quit := make(chan os.Signal)
    signal.Notify(quit, os.Interrupt)
    <-quit
    log.Println("shutdown server...")

    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    //关机失败原因
    if err := srv.Shutdown(ctx); err != nil {
        log.Fatal("server shutdown:", err)
    }

    log.Println("server exiting")
}


gin框架如何删除session_Powered by 金山文档_18


  1. 路由组
func main() {
    r := gin.Default()

    v1 := r.Group("/v1")
    {
        v1.GET("/aa", loginEndpoint)
        v1.GET("/bb", exit)
    }
    r.Run()
}

func loginEndpoint(c *gin.Context) {
    c.String(200,"hello")
}

func exit( context2 *gin.Context)  {
    context2.String(200,"world")
}


gin框架如何删除session_Powered by 金山文档_19


gin框架如何删除session_JSON_20


  1. 记录日志到文件和控制台(默认直接打印在控制台)
func main() {
    //禁用控制台颜色,将日志写入文件时不需要控制台颜色(加不加无所谓)
    //gin.DisableConsoleColor()

    //创建日志文件
    fileName, _ := os.Create("gin.log")

    //将日志并发写入文件
    gin.DefaultWriter = io.MultiWriter(fileName)

    //将日志同时写入文件和控制台
    //gin.DefaultWriter = io.MultiWriter(fileName, os.Stdout)

    r := gin.Default()
    r.GET("/ping", func(c *gin.Context) {
        c.String(200, "pong")
    })
    r.Run()
}


gin框架如何删除session_JSON_21


  1. html渲染模版
  • 默认的解析标签是{{}},可以用 r.Delims("{", "}")修改
  • LoadHTMLGlob 加载的目录下不能有图片等非模版文件,可以用tmpl,html,htm
  • 存在同名文件时,必须用define 关键字进行文件开头申明{{define "user/a.html"}},{{end}}结束申明,否则报错文件未申明;html/template: "user/a.html" is undefined
func main() {
    r := gin.Default()

    //LoadHTMLGlob 加载的目录下不能有图片等非模版文件
    //r.LoadHTMLGlob("views/*")
    //r.LoadHTMLGlob("user/*")

    //加载不是同一个目录下的模版文件
    //r.LoadHTMLFiles("views/a.html","user/a.html")

    //自定义变量解析符号
    r.Delims("{", "}")
    r.SetFuncMap(template.FuncMap{
        "customDate2": customDate,
    })

    r.LoadHTMLFiles("views/a.html")

    r.GET("/index", func(c *gin.Context) {
        c.HTML(http.StatusOK, "index.tmpl", gin.H{
            "title": "this is title",
            "body":  "this is views/index.tmpl",
        })
    })

    r.GET("/a", func(c *gin.Context) {
        c.HTML(http.StatusOK, "a.html", gin.H{
            "title": "this is a title",
            "body":  "this is views/a.html",
        })
    })

    r.GET("/date", func(c *gin.Context) {
        c.HTML(http.StatusOK, "a.html", map[string]interface{}{
            "now_time": time.Date(2023, 02, 02, 22, 33, 44, 55, time.UTC),
        })
    })

    //存在同名文件时,必须用define 关键字进行文件开头申明{{define "user/a.html"}},{{end}}结束申明
    //否则报错文件未申明;html/template: "user/a.html" is undefined
    r.GET("/aa", func(c *gin.Context) {
        c.HTML(http.StatusOK, "user/a.html", gin.H{
            "title": "this is a title",
            "body":  "this is user/a.html",
        })
    })

    r.Run()
}

//自定义时间
func customDate(t time.Time) string {
    y, m, d := t.Date()
    return fmt.Sprintf("%d-%02d-%02d", y, m, d)

    //y, m, d ,h,i,s:=time.Date()
    //return fmt.Sprintf("%d-%02d-%02d %d:%d:%d", y, m, d,h,s,i)
}

view/a.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
<!--    自定义变量解析符号-->
    <title>{.title}</title>
</head>
<body>
<h2>{.body}</h2>
Date: {.now_time}
<br>
Date2: {.now_time|customDate2}
</body>
</html>

user/a.html

{{define "user/a.html"}}
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>{{.title}}</title>
</head>
<body>
<h2>{{.body}}</h2>
</body>
</html>
{{end}}


gin框架如何删除session_gin框架如何删除session_22


  1. http2 server 推送
  2. jsonp 发送数据
//jsonp
func main() {
    r := gin.Default()

    r.GET("/jsonp", func(c *gin.Context) {
        data := map[string]interface{}{
            "name": "lxw",
        }
        c.JSONP(http.StatusOK, data)
    })
    r.Run()
}

//context包中callback 默认写法
func (c *Context) JSONP(code int, obj any) {
    callback := c.DefaultQuery("callback", "")
    if callback == "" {
        c.Render(code, render.JSON{Data: obj})
        return
    }
    c.Render(code, render.JsonpJSON{Callback: callback, Data: obj})
}


gin框架如何删除session_gin框架如何删除session_23


gin框架如何删除session_Powered by 金山文档_24


16. Multipart/Urlencoded 绑定,参数绑定验证(登录验证是使用)

type LoginForm struct {
    User string `form:"user" binding:"required"`
    Pwd  string `form:"password" binding:"required"`
}

func main() {
    r := gin.Default()
    r.POST("/login", func(c *gin.Context) {
        var form LoginForm
        if err := c.ShouldBind(&form); err == nil {
            //验证用户密码是否匹配
            if form.User == "张三" && form.Pwd == "123456" {
                c.JSON(200, gin.H{"msg": "login ok!"})
            } else {
                c.JSON(401, gin.H{"msg": "login fail"})
            }
        } else {
            c.JSON(401, gin.H{"msg": "参数缺失", "err": err.Error()})
        }
    })
    r.Run()
}


gin框架如何删除session_Powered by 金山文档_25


传入空数据


gin框架如何删除session_Powered by 金山文档_26


如果给错字段名:password 给错password2


gin框架如何删除session_gin_27


Multipart/Urlencoded 表单:使用场景哪些?

func main() {
    r := gin.Default()

    r.POST("/form_post", func(c *gin.Context) {
        message := c.PostForm("message")
        nickName := c.DefaultPostForm("nick", "默认名")
        c.JSON(200, gin.H{"status": http.StatusOK, "message": message, "nick": nickName})
    })

    r.Run()
}


gin框架如何删除session_github_28


17.只绑定 url 查询字符串(只打印绑定的参数)

type Person12 struct {
    Name string `form:"name"`
    Addr string `form:"addr"`
}
func startPage12(c *gin.Context) {
    var person Person12
    if err:=c.ShouldBind(&person);err == nil {
        log.Println("只打印get请求参数:",person.Name, person.Addr)
    }else{
        log.Printf("错误:%v",err.Error())
    }
    c.String(200, "success")
}
func main() {
    r := gin.Default()
    r.Any("/test", startPage12)
    r.Run()
}


gin框架如何删除session_Powered by 金山文档_29


18.获取路径中的参数

func main()  {
    r:=gin.Default()
    // http://0.0.0.0:8080/user/lxw
    r.GET("/user/:name", func(c *gin.Context) {
        name:=c.Param("name")
        c.String(http.StatusOK,"hello %s",name)
    })

    //匹配:http://0.0.0.0:8080/user/lxw/read
    r.GET("/user/:name/:action", func(c *gin.Context) {
        name:=c.Param("name")
        action:=c.Param("action")
        //message:=name+action
        c.String(http.StatusOK,name+" is "+action)
    })
    r.Run()
}


gin框架如何删除session_gin框架如何删除session_30


gin框架如何删除session_gin框架如何删除session_31


gin框架如何删除session_gin_32


  1. purejson(没有明确这个东西存在意义是啥?区别?)
func main()  {
    r:=gin.Default()
    // 提供 unicode 实体
    r.GET("/test", func(c *gin.Context) {
        c.JSON(200,gin.H{
            "echo":"<h1>hello lxw</h1>",
        })
    })

    // 提供字面字符
    r.GET("/test2", func(c *gin.Context) {
        c.PureJSON(200,gin.H{
            "echo":"<h1>hello lxw</h1>",
        })
    })
    r.Run()
}


gin框架如何删除session_JSON_33


gin框架如何删除session_Powered by 金山文档_34


17.post 表单中的url参数

//post 表单中的url参数
func main()  {
    r:=gin.Default()
    r.POST("/test", func(c *gin.Context) {
        id:=c.Query("id")
        page:=c.DefaultQuery("page","0")
        name:=c.PostForm("name")
        fmt.Printf("id: %s; page: %s; name: %s", id, page, name)
        fmt.Println("ok")
        c.JSON(200,"log ok")
    })

    r.Run()
}


gin框架如何删除session_Powered by 金山文档_35


18.重定向(http 重定向&路由重定向)

func main()  {
    r:=gin.Default()
    //http 重定向
    r.GET("/test4", func(c *gin.Context) {
        c.Redirect(http.StatusMovedPermanently,"http://www.baidu.com/")
    })

    r.GET("/test2", func(c *gin.Context) {
        c.JSON(200,gin.H{"11":"11"})
        c.Request.URL.Path="/test3"
        r.HandleContext(c)
        c.JSON(200,gin.H{"111":"22"})
    })
     //http://0.0.0.0:8081/test2   {"11":"11"}{"name":"lxw"}{"111":"22"}

    r.GET("/test3", func(c *gin.Context) {
        c.JSON(200,gin.H{"name":"lxw"})
    })
    r.Run(":8081")
}

http://0.0.0.0:8081/test4 重定向跳转到baidu.com 页面


gin框架如何删除session_github_36


19.SecureJSON

func main() {
    r := gin.Default()
    //r.SecureJsonPrefix("test_") //输出:test_["lxw","zh","cha"]

    r.GET("/test", func(c *gin.Context) {
        names := []string{"lxw", "zh", "cha"}
        c.SecureJSON(http.StatusOK, names)
    })
    //不带前缀:
    /*while(1);[
     "lxw",
     "zh",
     "cha"
    ]*/

    r.Run(":8081")
}


gin框架如何删除session_Powered by 金山文档_37


gin框架如何删除session_github_38


20.从reader读取数据

func main() {
    r := gin.Default()
    r.GET("/read", func(c *gin.Context) {
        //读取文件到response
        response, err := http.Get("https://p0.ssl.qhimgs1.com/sdr/400__/t01043eec7c54708f40.jpg")
        if err != nil || response.StatusCode != http.StatusOK {
            c.Status(http.StatusServiceUnavailable)
        }
        
        //获取文件主体 长度 类型
        reader := response.Body
        contentLength := response.ContentLength
        contentType := response.Header.Get("Content-Type")
        //生成文件头
        extraHeader := map[string]string{
            "Content-Disposition": `attachment;filename="gopher.jpg"`, //给文件重命名
        }
        c.DataFromReader(http.StatusOK, contentLength, contentType, reader, extraHeader)
    })

    r.Run(":8081")
}


gin框架如何删除session_github_39


21.静态文件服务

func main() {
    r := gin.Default()
    r.Static("/static", "./views")                 //访问view文件夹下的所有文件
    r.StaticFS("/more_static", http.Dir("views")) //访问view文件夹下的所有文件
    r.StaticFile("/1.jpeg", "./1.jpeg")           //z访问单个文件
    r.Run()
}

参考:https://learnku.com/docs/gin-gonic/1.7/examples-bind-uri/11391