gin 框架初始教程文档

news2024/11/26 21:38:31

一 、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.效果图

二、基础知识

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

为什么 浏览器和 终端命令行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>
  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   

josn:

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

结果:success

效果图:

  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")

}

  1. 控制log 高亮输出,默认开启高亮

//强制log 高亮
gin.ForceConsoleColor()

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

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

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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/373148.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java 一文讲透面向对象 (20万字博文)

目录 一、前言 二、面向对象程序设计介绍 1.oop三大特性 : 2.面向对象和面向过程的区别 : 3.面向对象思想特点 : 4.面向对象的程序开发 : 三、Java——类与对象 1.java中如何描述一个事物? 2.什么是类? 3.类的五大成员&#xff1a; 4.封装的前提——抽象 : 5.什么是对…

使用python暴力破解zip压缩包的密码

如果你有压缩包的密码忘记了&#xff0c;并且压缩包的加密算法采用的是ZipCrypto&#xff0c;并且压缩参数如下图所示&#xff1a; 那么你就可以使用本文中的方法进行破解。 压缩包的加密&#xff0c;是根据输入的密码进行运算加密&#xff0c;输入不同的密码&#xff0c;加密…

OpenCV-Python系列(二)—— 图像处理(灰度图、二值化、边缘检测、高斯模糊、轮廓检测)

一、【灰度图、二值化】 import cv2 img cv2.imread("lz2.png") gray_img cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 灰度图 # 二值化&#xff0c;(127,255)为阈值 retval,bit_img cv2.threshold(gray_img, 127, 255, cv2.THRESH_BINARY) cv2.imshow(photo1,im…

java:自定义变量加载到系统变量后替换shell模版并执行shell

这里的需求前提是&#xff0c;在项目中进行某些操作前&#xff0c;需要在命令后对shell配置文件的进行修改&#xff08;如ip、port&#xff09;&#xff0c;这个对于用户是不友好的&#xff0c;需要改为用户页面输入ip、port&#xff0c;后台自动去操作修改配置&#xff1b;那么…

SAP 能不能撤销已冲销的凭证?能的话怎么操作?

本篇涉及SAP的SAP 财务凭证的冲销操作的。有需要可以查看&#xff1a;前面的文章有一篇是专门介绍如何介绍如何冲销SAP财务凭证的文章。SAP财务凭证常见的冲销步骤详细操作手册&#xff08;FB08、AB08、VF11、FBRA等&#xff09; 开始进入正题。假如有这么一种场景&#xff1a;…

【Acwing 周赛复盘】第92场周赛复盘(2023.2.25)

【Acwing 周赛复盘】第92场周赛复盘&#xff08;2023.2.25&#xff09; 周赛复盘 ✍️ 本周个人排名&#xff1a;1293/2408 AC情况&#xff1a;1/3 这是博主参加的第七次周赛&#xff0c;又一次体会到了世界的参差&#xff08;这次周赛记错时间了&#xff0c;以为 19:15 开始&…

使用Jmeter进行性能测试的这套步骤,入职京东后,涨薪2次,升职一次

项目背景&#xff1a; 我们的平台为全国某行业监控平台&#xff0c;经过3轮功能测试、接口测试后&#xff0c;98%的问题已经关闭&#xff0c;决定对省平台向全国平台上传数据的接口进行性能测试。 01 测试步骤 1、编写性能测试方案 由于我是刚进入此项目组不久&#xff0c;…

怎么设置启用远程桌面?如何让外网电脑远程本地内网?

如何远程控制电脑&#xff1f;最简单实用的方案是开启电脑系统自带的远程桌面功能&#xff0c;如果涉及跨网、内外网互通&#xff0c;可以同时用快解析内网映射外网。下面是方案的具体实施步骤&#xff0c;供大家参考。 怎么打开设置启用远程桌面&#xff1f; 1.在目标需要远…

IO:阻塞和非阻塞、同步和异步

阻塞和非阻塞 阻塞的时候线程会被挂起 阻塞&#xff1a; 当数据还没准备好时&#xff0c;调用了阻塞的方法&#xff0c;则线程会被挂起&#xff0c;会让出CPU时间片&#xff0c;此时是无法处理过来的请求&#xff0c;需要等待其他线程来进行唤醒&#xff0c;该线程才能进行后续…

Javascript的API基本内容(二)

一、事件监听 结合 DOM 使用事件时&#xff0c;需要为 DOM 对象添加事件监听&#xff0c;等待事件发生&#xff08;触发&#xff09;时&#xff0c;便立即调用一个函数。 addEventListener 是 DOM 对象专门用来添加事件监听的方法&#xff0c;它的两个参数分别为【事件类型】和…

产业链金融的前世今生

产业链金融脱胎于供应链金融&#xff0c;又不同于供应链金融。二者的区别是&#xff0c; 供应链金融服务于单个环节、单个企业&#xff0c;而产业链金融是以产业链的核心 企业为依托&#xff0c;针对产业链的各个环节&#xff0c;设计个性化、标准化的金融服务产品&#xff0c;…

Appium自动化测试框架是一种较为优雅的使用方式

以操作小米商城下单为例流程是 启动小米商城app, 点击分类&#xff0c;点击小米手机&#xff0c; 点击小米10 至尊版&#xff0c;点击加入购物车&#xff0c;点击确定....原脚本Copyfrom time import sleep from appium import webdriver from selenium.common.exceptions impo…

python有哪些应用方向及其学习方法 资源推荐

目录 python 语言的应用方向 python简介 1.常规软件开发 2.科学计算 3.自动化运维 4.云计算 5.WEB开发 6.网络爬虫 7.大数据分析 8.人工智能 9.python处理图片和视频 【渗透测试相关工具下载】 推荐阅读 python实战文章 渗透测试文章 渗透测试实战专栏 python黑…

Gorm-学习笔记

1 基本使用 2 创建数据 2.1 如何使用Upsert 使用clause.OnConflict处理数据冲突 2.2 如何使用默认值 通过使用default标签为字段定义默认值 3 查询数据 3.1 First与Find 使用First时&#xff0c;需要注意查询不到数据会返回ErrRecordNotFound。 使用Find查询多条数据&#x…

详讲函数.2.

目录 5. 函数的嵌套调用和链式访问 5.1 嵌套调用 5.2 链式访问 小结&#xff1a; 6. 函数的声明和定义 6.1 函数的声明&#xff1a; 6.2 函数的定义&#xff1a; 5. 函数的嵌套调用和链式访问 函数和函数之间可以根据实际的需求进行组合的&#xff0c;也就是互相调用的…

JUC包:CountDownLatch源码+实例讲解

1 缘起 有一次听到同事谈及AQS时&#xff0c;我有很多点懵&#xff0c; 只知道入队和出队&#xff0c;CLH&#xff08;Craig&#xff0c;Landin and Hagersten&#xff09;锁&#xff0c;并不了解AQS的应用&#xff0c; 同时结合之前遇到的多线程等待应用场景&#xff0c;发现…

QML 元素布局

定位器&#xff1a;是QtQuick模块中的提供的&#xff0c;有以下三种 Row 行定位器Column 列定位器Grid 网格定位器Flow 流动定位器常用属性: spacing间距 Row &#xff08;行定位器&#xff09; 按照行的方排列 //行定位器 Row{spacing: 5//设置间距Rectangle{width: 100he…

[蓝桥杯] 二分与前缀和习题练习

文章目录 一、二分查找习题练习 1、1 数的范围 1、1、1 题目描述 1、1、2 题解关键思路与解答 1、2 机器人跳跃问题 1、2、1 题目描述 1、2、2 题解关键思路与解答 1、3 四平方和 1、3、1 题目描述 1、3、2 题解关键思路与解答 二、前缀和习题练习 2、1 前缀和 2、1、1 题目描述…

《操作系统》——第二章 进程与线程

目录 2.1.1进程的概念、组成、特征 2.1.2进程的状态与转换、进程的组织 2.1.3进程控制 2.1.4进程通信 2.1.5线程的概念 2.1.6线程的实现方式和多线程模型 2.2.1调度的概念、层次 2.2.2进程调度的时机、切换与过程、方式 2.2.4调度算法的评价指标 2.2.5调度算法(1) 2…

1249 亲戚(并查集)

1249. 亲戚 题目 提交记录 讨论 题解 视频讲解或许你并不知道&#xff0c;你的某个朋友是你的亲戚。 他可能是你的曾祖父的外公的女婿的外甥女的表姐的孙子。 如果能得到完整的家谱&#xff0c;判断两个人是否是亲戚应该是可行的&#xff0c;但如果两个人的最近公共祖…