• Golang | Web开发之Gin框架快速入门基础实践


    欢迎关注「全栈工程师修炼指南」公众号

    点击 👇 下方卡片 即可关注我哟!

    设为星标⭐每天带你 基础入门 到 进阶实践 再到 放弃学习

    专注 企业运维实践、网络安全、系统运维、应用开发、物联网实战、全栈文章 等知识分享

      花开堪折直须折,莫待无花空折枝 


    作者主页:[ https://www.weiyigeek.top ]  

    博客:[ https://blog.weiyigeek.top ]

    作者<开发安全运维>学习交流群,回复【学习交流群】即可加入


    文章目录:

    5a5c58f66c9e492cac2a702366b05238.png


    0x00 前言简述

    描述: 通过上一阶段的Go语言的基础学习,相信各位看友有一定的Go语言开发的基础,今天将给大家引入Go语言的Web框架,但是在介绍此之前,我们先了解前面我们学习的后端语言通常都有相依赖的Web框架, 例如 PHP 的 Laravel、THinkPHP、Yii等框架 ,Java 的 Spring Boot 、Quarkus、Micronaut、Jakarta EEVert.x 等框架,作为一名Google推出的Go语言来说也拥有众多的Web框架,例如今天讲解的Gin框架,以及 Aero、Beego 、Iris、Echo、Revel、Martini 等众多优秀的Web框架, 其各有各的特点。

    总之,选择正确的Go Web Framework对于交付高质量和高效率的网络应用程序至关重要。

    1.什么是Gin?

    描述: Gin 是一个用 Go (Golang) 编写的 web 框架, 它提供类似Martini的API,但性能更佳,速度提升高达40倍, 号称是Go语言最快的全功能Web框架,所以如果你是性能和高效的追求者,你会爱上 Gin.

    f794cfca8ffd938c1a4bd876b4f84073.jpeg

    官网地址: https://gin-gonic.com/
    帮助文档: https://gin-gonic.com/zh-cn/docs/
    Go Web框架基准测试: https://github.com/gin-gonic/gin/blob/master/BENCHMARKS.md

    7c48b9d4cce450ea0991056267d19066.png

    2.有啥特点Gin?

    描述: 其主要特点可以归纳终结为一下所述。
    快速 : 基于 Radix 树的路由,小内存占用、没有反射、可预测的 API 性能。
    路由组 : 帮助您更好地组织您的路由,例如,按照需要授权和不需要授权和不同API版本进行分组,此外路由分组可以无限嵌套而不降低性能。
    内置渲染: 其为 JSON,XML 和 HTML 渲染提供了易于使用的 API。
    JSON 解析: 解析并验证请求的 JSON,例如检查所需值的存在。
    中间件 处理 : 传入的 HTTP 请求可以由一系列中间件和最终操作来处理, 例如:Logger,Authorization,GZIP,最终操作 DB。
    Crash 处理 : 可以 catch 一个发生在 HTTP 请求中的 panic 并 recover 它。这样你的服务器将始终可用例如,你可以向 Sentry 报告这个 panic!
    Error 处理: 支持收集 HTTP 请求期间发生的所有错误。最终,中间件可以将它们写入日志文件,数据库并通过网络发送。
    可扩展性: 新建一个中间件非常简单,去查看示例代码吧

    3.如何下载安装Gin?

    描述: 由于Gin是基于Golang开发的所以必须得先安装Go环境,如果你还没安装此环境可以参考博主此系列教程文章【1.Go编程快速入门学习】( https://blog.weiyigeek.top/2020/4-23-283.html#0x01-Go语言开发环境搭建),下述操作都是假设你已经安装配置好Go语言相关环境情况下进行的。

    温馨提示: Gin 必须是在Go 1.13 及以上版本上运行,当然我相信大家用的不会这么老的版本的。

    在 Windows 操作系统开发

    1. # 启用模块以及配置模块拉取镜像
    2. PS C:\Users\WeiyiGeek> $env:GO111MODULE = "on"
    3. PS C:\Users\WeiyiGeek> $env:GOPROXY = "https://goproxy.cn,direct"
    4. # 拉取最新的gin模块包
    5. PS C:\Users\WeiyiGeek> go get -u -v github.com/gin-gonic/gin

    在 Linux 操作系统开发

    1. # 启用模块以及配置模块拉取镜像
    2. $ GO111MODULE = "on"
    3. $ GOPROXY = "https://goproxy.cn,direct"
    4. # 拉取最新的gin模块包
    5. $ go get -u -v github.com/gin-gonic/gin

    0x01 快速上手

    描述: 作者为了方便看友们快速上手,此处将其常规使用方法函数进行示例演示,为加深学习印象与成果。

    1.Hello-World 示例

    描述: 下载安装 Gin 软件包后创建一个 hello-gin 项目文件夹以及main.go文件,此处作者以一个简单的hello woirld示例来讲解。

    创建文件夹: $ mkdir -p $GOPATH/src/github.com/weiyigeek/hello-gin && cd "$_"

    1. // hello-gin/main.go
    2. // 声明当前文件属于哪个包,如果是主文件则写成main
    3. package main
    4. // 导入gin包
    5. import "github.com/gin-gonic/gin"
    6. func main() {
    7. // 生成了一个实例,这个实例即 WSGI 应用程序
    8. r := gin.Default()
    9. // 声明了一个GET方法路由 / 及对应的处理函数
    10. r.GET("/", func(c *gin.Context) {
    11. c.String(200, "Hello, Go Gin Web!")
    12. })
    13. // 返回以JSON格式的字符串
    14. r.GET("/test", func(c *gin.Context) {
    15. c.JSON(200, gin.H{
    16. "msg": "Test, Go Gin Web!",
    17. })
    18. })
    19. // 除了GET方法以外,GIN还支持的其他HTTP方法如下;
    20. r.POST("/somePost", func(c *gin.Context) { c.JSON(200, gin.H{ "msg": "posting",}) })
    21. r.PUT("/somePut", func(c *gin.Context) { c.JSON(200, gin.H{ "msg": "putting",}) })
    22. r.DELETE("/someDelete", func(c *gin.Context) { c.JSON(200, gin.H{ "msg": "deleting",}) })
    23. r.PATCH("/somePatch", func(c *gin.Context) { c.JSON(200, gin.H{ "msg": "patching",}) })
    24. r.HEAD("/someHead", func(c *gin.Context) { c.JSON(200, gin.H{ "msg": "head",}) })
    25. r.OPTIONS("/someOptions", func(c *gin.Context) { c.JSON(200, gin.H{ "msg": "options",}) })
    26. // 特殊的 Any 函数即响应所有HTTP方法
    27. r.Any("/testing", func(c *gin.Context) { c.JSON(200, gin.H{ "msg": "Any registers a route that matches all the HTTP methods. EX: GET, POST, PUT, PATCH, HEAD, OPTIONS, DELETE, CONNECT, TRACE.",}) })
    28. // 让应用运行在本地服务器上,默认监听端口是 8090
    29. g.Run(":8080") // listen and serve on 0.0.0.0:8080
    30. }

    初始化及编写和执行 Go 代码

    1. # 初始化
    2. go mod init hello-gin
    3. go mod tidy # 英 / 'taɪdi'
    4. # 运行
    5. $ go run main.go

    1647f50a23ef7a8e825d40365bd5fcaf.png

    2.JSON/XML/YAML/ProtoBuf格式渲染示例

    描述: 本小节演示了AsciiJSON、JSON、purejson以及JSONP与SecureJSON示例以及XML/YAML/ProtoBuf渲染显示。

    1. package main
    2. import (
    3. "net/http"
    4. "github.com/gin-gonic/gin"
    5. )
    6. // 开辟一块内存空间
    7. var data = make(map[string]interface{})
    8. func setupRouter() *gin.Engine {
    9. // Disable Console Color
    10. gin.DisableConsoleColor()
    11. // Default返回一个Engine实例,该实例已连接Logger和Recovery中间件。
    12. r := gin.Default()
    13. // 示例1.AsciiJSON 生成具有转义的非 ASCII 字符的 ASCII-only JSON。
    14. r.GET("/AsciiJSON", func(c *gin.Context) {
    15. data = map[string]interface{}{
    16. "lang": "GO语言",
    17. "tag": "
      "
      ,
    18. }
    19. // 针对中文以及特殊符号进行的是Unicode编码
    20. // 输出 : {"lang":"GO\u8bed\u8a00","tag":"\u003cbr\u003e"}
    21. c.AsciiJSON(http.StatusOK, data)
    22. })
    23. // 示例2.通常,JSON 使用 unicode 替换特殊 HTML 字符,例如 < 变为 \ u003c。如果要按字面对这些字符进行编码,则可以使用 PureJSON。
    24. // 提供 unicode 实体
    25. r.GET("/json", func(c *gin.Context) {
    26. // 温馨提示: gin.H 是 map[string]interface{} 的一种快捷方式
    27. c.JSON(http.StatusOK, gin.H{
    28. "html": "Hello, world! Go-Gin 框架",
    29. })
    30. })
    31. // 提供字面字符(即原始数据未经过编码处理)
    32. r.GET("/purejson", func(c *gin.Context) {
    33. c.PureJSON(http.StatusOK, gin.H{
    34. "html": "Hello, world! Go-Gin 框架",
    35. })
    36. })
    37. // 示例3.使用 JSONP 向不同域的服务器请求数据,如果查询参数存在回调,则将回调添加到响应体中。
    38. r.GET("/JSONP", func(ctx *gin.Context) {
    39. data = map[string]interface{}{
    40. "name": "WeiyiGeek",
    41. "site": "weiyigeek.top",
    42. }
    43. // /JSONP?callback=u 其默认参数是callback
    44. // 将输出:u({\"name\":\"WeiyiGeek\",\"site\":\"weiyigeek.top\"})
    45. ctx.JSONP(http.StatusOK, data)
    46. })
    47. // 示例4.使用 SecureJSON 防止 json 劫持,如果给定的结构是数组值,则默认预置 "while(1)," 到响应体。
    48. r.GET("/SecureJSON", func(c *gin.Context) {
    49. // 你也可以使用自己的 SecureJSON 前缀,据说Google采用的是while的方法,facebook采用的是for的方法。
    50. r.SecureJsonPrefix(")]}',\n")
    51. names := []string{"Go", "Java", "PHP"}
    52. // 若不设置SecureJsonPrefix,默认输出:for(;;);["Go", "Java", "PHP"]
    53. c.SecureJSON(http.StatusOK, names)
    54. })
    55. return r
    56. }
    57. func main() {
    58. r := setupRouter()
    59. // Listen and Server in 0.0.0.0:8080
    60. r.Run(":8080")
    61. }

    代码运行访问结果:
    bfae4147f02eeec5cabef1c9280a2239.png

    补充说明: Gin请求响应XML/JSON/YAML/ProtoBuf格式输出示例片段

    1. // 方式1.JSON 格式方法1, 温馨提示 gin.H 是 map[string]interface{} 的一种快捷方式
    2. r.GET("/someJSON", func(c *gin.Context) {
    3. c.JSON(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
    4. })
    5. // 方式2.JSON 格式方法2,
    6. r.GET("/AsciiJSON", func(c *gin.Context) {
    7. data = map[string]interface{}{
    8. "lang": "Golang",
    9. "tag": "gin",
    10. }
    11. c.AsciiJSON(http.StatusOK, data)
    12. })
    13. // 方式3.JSON 格式方法3
    14. r.GET("/moreJSON", func(c *gin.Context) {
    15. // 你也可以使用一个结构体
    16. var msg struct {
    17. Name string `json:"user"`
    18. Message string
    19. Number int
    20. }
    21. msg.Name = "Lena"
    22. msg.Message = "hey"
    23. msg.Number = 123
    24. // 注意 msg.Name 在 JSON 中变成了 "user"
    25. // 将输出:{"user": "Lena", "Message": "hey", "Number": 123}
    26. c.JSON(http.StatusOK, msg)
    27. })
    28. // XML 格式输出
    29. r.GET("/someXML", func(c *gin.Context) {
    30. c.XML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
    31. })
    32. // YAML 格式输出
    33. r.GET("/someYAML", func(c *gin.Context) {
    34. c.YAML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
    35. })
    36. // ProtoBuf 格式输出
    37. r.GET("/someProtoBuf", func(c *gin.Context) {
    38. reps := []int64{int64(1), int64(2)}
    39. label := "test"
    40. // protobuf 的具体定义写在 testdata/protoexample 文件中。
    41. data := &protoexample.Test{
    42. Label: &label,
    43. Reps: reps,
    44. }
    45. // 请注意,数据在响应中变为二进制数据
    46. // 将输出被 protoexample.Test protobuf 序列化了的数据
    47. c.ProtoBuf(http.StatusOK, data)
    48. })

    3.Basic-Auth 方法示例

    代码示例:

    1. package main
    2. import (
    3. "fmt"
    4. "net/http"
    5. "github.com/gin-gonic/gin"
    6. )
    7. var db = make(map[string]string)
    8. // 或者模拟一些私人数据
    9. var secrets = gin.H{
    10. "foo": gin.H{"email": "foo@bar.com", "phone": "123433"},
    11. "weiyigeek": gin.H{"email": "master@weiyigeek.top", "phone": "666"},
    12. }
    13. func setupRouter() *gin.Engine {
    14. // Disable Console Color
    15. // gin.DisableConsoleColor()
    16. r := gin.Default()
    17. // 1.Query 和 POST form 请求示例
    18. // Ping test
    19. r.GET("/ping", func(c *gin.Context) {
    20. c.String(http.StatusOK, "pong")
    21. })
    22. // 方式1.Get user value, example : /user/weiyigeek
    23. r.GET("/user/:name", func(c *gin.Context) {
    24. user := c.Params.ByName("name")
    25. value, ok := db[user]
    26. fmt.Println(user)
    27. if ok {
    28. c.JSON(http.StatusOK, gin.H{"user": user, "value": value})
    29. } else {
    30. c.JSON(http.StatusOK, gin.H{"user": user, "status": "no value"})
    31. }
    32. })
    33. // 方式2.Get user value, example : /username?id=1&user=weiyigeek&page=1
    34. r.GET("/username", func(c *gin.Context) {
    35. id := c.Query("id")
    36. user := c.Query("user")
    37. page := c.DefaultQuery("page", "0")
    38. if id != "" && user != "" || page != "" {
    39. c.JSON(http.StatusOK, gin.H{"code": "200", "data": gin.H{"id": id, "user": user, "page": page}})
    40. } else {
    41. c.JSON(http.StatusBadRequest, gin.H{"code": "0", "status": "err"})
    42. }
    43. })
    44. // 方式3.POST user value, example :
    45. // POST /username
    46. // id=1&user=weiyigeek&page=10
    47. r.POST("/username", func(c *gin.Context) {
    48. id := c.PostForm("id")
    49. user := c.PostForm("user") // 表单数据
    50. page := c.DefaultPostForm("page", "10") // 默认值
    51. if secret, ok := secrets[user]; ok {
    52. c.JSON(http.StatusOK, gin.H{"id": id, "user": user, "page": page, "secret": secret})
    53. } else {
    54. c.JSON(http.StatusForbidden, gin.H{"user": user, "secret": "NO SECRET :("})
    55. }
    56. })
    57. // 路由组使用 gin.BasicAuth() 中间件设置 Auth 认证访问
    58. // gin.Accounts 是 map[string]string 的一种快捷方式
    59. // 方式1
    60. // authorized := r.Group("/create")
    61. // authorized.Use(gin.BasicAuth(gin.Credentials{
    62. // "foo": "bar",
    63. // "weiyieek": "123456",
    64. //}))
    65. // 方式2
    66. authorized := r.Group("/admin", gin.BasicAuth(gin.Accounts{
    67. "foo": "bar", // user:foo password:bar
    68. "weiyigeek": "123456", // user:manu password:123
    69. }))
    70. // admin/secrets 端点 触发 "localhost:8080/admin/secrets
    71. /* example curl for /admin with basicauth header
    72. Zm9vOmJhcg== is base64("weiyieek:123456")
    73. JS -> btoa("weiyigeek:123456") => 'd2VpeWlnZWVrOjEyMzQ1Ng=='
    74. curl -X POST http://localhost:8080/admin/secrets \
    75. -H 'authorization: Basic d2VpeWlnZWVrOjEyMzQ1Ng==' \
    76. -H 'content-type: application/json' \
    77. -d '{"value":"weiyigeek.top"}'
    78. */
    79. authorized.POST("secrets", func(c *gin.Context) {
    80. // 获取用户,它是由 BasicAuth 中间件设置的
    81. user := c.MustGet(gin.AuthUserKey).(string)
    82. // 解析提交的JSON数据(Parse JSON)
    83. var json struct {
    84. Value string `json:"value" binding:"required"`
    85. }
    86. // 将提交的数据与对应用户绑定
    87. if c.Bind(&json) == nil {
    88. db[user] = json.Value
    89. c.JSON(http.StatusOK, gin.H{"status": "ok", "code": "200"})
    90. } else {
    91. c.JSON(http.StatusBadRequest, gin.H{"status": "err", "code": "0"})
    92. }
    93. })
    94. // authorized POST username value
    95. authorized.POST("/user", func(c *gin.Context) {
    96. // 获取用户,它是由 BasicAuth 中间件设置的
    97. user := c.MustGet(gin.AuthUserKey).(string)
    98. // 根据authorization头,获取自定义数据
    99. if secret, ok := secrets[user]; ok {
    100. c.JSON(http.StatusOK, gin.H{"user": user, "secret": secret})
    101. } else {
    102. c.JSON(http.StatusForbidden, gin.H{"user": user, "secret": "NO SECRET :("})
    103. }
    104. })
    105. return r
    106. }
    107. func main() {
    108. r := setupRouter()
    109. // Listen and Server in 0.0.0.0:8080
    110. r.Run(":8080")
    111. }

    执行结果:

    1. # 正式环境设置环境变量与代码
    2. [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
    3. - using env: export GIN_MODE=release
    4. - using code: gin.SetMode(gin.ReleaseMode)
    5. [GIN-debug] GET /ping --> main.setupRouter.func1 (3 handlers)
    6. [GIN-debug] GET /user/:name --> main.setupRouter.func2 (3 handlers)
    7. [GIN-debug] GET /username --> main.setupRouter.func3 (3 handlers)
    8. [GIN-debug] POST /username --> main.setupRouter.func4 (3 handlers)
    9. [GIN-debug] POST /admin/secrets --> main.setupRouter.func5 (4 handlers)
    10. [GIN-debug] POST /admin/user --> main.setupRouter.func6 (4 handlers)
    11. [GIN-debug] Listening and serving HTTP on :8080
    12. # 1.在终端中运行curl进行POST请求写入值到auth用户中
    13. $ curl --location 'http://127.0.0.1:8080/admin/secrets' \
    14. --header 'authorization: Basic d2VpeWlnZWVrOjEyMzQ1Ng==' \
    15. --header 'content-type: application/json' \
    16. --data '{"value":"weiyigeek.top"}'
    17. # {"code":"200","status":"ok"}
    18. # 2.GET请求通过url方法Params获取用户字段信息并返回该auth用户写入信息。
    19. curl --location 'http://127.0.0.1:8080/user/weiyigeek'
    20. # {"user": "weiyigeek", "value": "weiyigeek.top" }
    21. # 3.GET请求通过url参数获取用户字段并返回其kv。
    22. curl --location 'http://127.0.0.1:8080/username?id=1&user=weiyigeek&page=10' \
    23. --header 'authorization: Basic Zm9vOmJhcg=='
    24. # {"code":"200","data":{"id":"1","page":"10","user":"weiyigeek"}}
    25. # 4.POST请求通过获取发送的表单数据获取用户字段并模拟一些私人数据。
    26. curl --location 'http://127.0.0.1:8080/username' \
    27. --header 'authorization: Basic Zm9vOmJhcg==' \
    28. --form 'id="1"' \
    29. --form 'user="weiyigeek"' \
    30. --form 'page="10"'
    31. # {"id":"1","page":"10","secret":{"email":"master@weiyigeek.top","phone":"666"},"user":"weiyigeek"}
    32. # 5.通过POST请求通过认证auth后获取用户字段并模拟一些私人数据
    33. curl --location --request POST 'http://127.0.0.1:8080/admin/user' \
    34. --header 'authorization: Basic d2VpeWlnZWVrOjEyMzQ1Ng=='
    35. # {"secret":{"email":"master@weiyigeek.top","phone":"666"},"user":"weiyigeek"}

    2d0274adfc1ee002562a173e3f332305.png

    4.路由、表单参数绑定示例

    描述: 在网页中往往需要从URL 路由参数、URL 请求参数、表单中提交数据到后端进行处理,例如最常见的就是登录时需要传递用户名与密码以及验证码,所以作为Gin这么优秀的Web框架也是支持处理表单数据的。

    请求参数获取总结:

    • Param("参数名") 方法: 获取路由参数

    • Query("参数名") 方法:获取URL参数

    • DefaultQuery("参数名","默认值") 方法:获取URL参数值,当不存在该参数时自动填充默认值

    • QueryMap("参数名") 方法:映射URL查询字符串

    • PostForm("参数名") 方法: 获取 POST 提交表单参数值

    • DefaultPostForm("参数名","默认值") 方法:获取 POST 提交表单参数值,当不存在该参数时自动填充默认值

    • PostFormMap("参数名") 方法:映射表单参数

    • ShouldBind(&结构体实例化对象) 方法:针对提交的GET请求或者POST请求参数自动进行绑定。

      // 如果是 GET 请求,只使用 Form 绑定引擎(query)。
      // 如果是 POST 请求,首先检查 content-type 是否为 JSON 或 XML,然后再使用 Form 或(form-data)。
      // 查看更多:https://github.com/gin-gonic/gin/blob/master/binding/binding.go#L88

    代码示例:

    1. package main
    2. import (
    3. "net/http"
    4. "github.com/gin-gonic/gin"
    5. )
    6. func setupRouter() *gin.Engine {
    7. // Disable Console Color
    8. gin.DisableConsoleColor()
    9. // Default返回一个Engine实例,该实例已连接Logger和Recovery中间件。
    10. r := gin.Default()
    11. // 示例1. 使用现有的基础请求对象解析查询字符串参数。
    12. // 示例 URL:/param_get?firstname=Weiyi&lastname=Geek
    13. r.GET("/param_get", func(c *gin.Context) {
    14. firstname := c.DefaultQuery("firstname", "Guest")
    15. lastname := c.Query("lastname") // 注意: c.Request.URL.Query().Get("lastname") 的一种快捷方式
    16. c.String(http.StatusOK, "Hello %s %s, Welcome To Study Go Gin!", firstname, lastname)
    17. })
    18. // 示例2.提交常规类型的数据表单并解析POST请求数据字符串参数。
    19. // Content-Type: application/x-www-form-urlencoded
    20. r.POST("/form_post", func(c *gin.Context) {
    21. name := c.PostForm("name")
    22. msg := c.DefaultPostForm("message", "empty")
    23. c.JSON(http.StatusOK, gin.H{"code": 200, "data": gin.H{"name": name, "message": msg}})
    24. })
    25. // 示例3.使用QueryMap、PostFormMap 函数映射查询字符串或表单参数
    26. // Content-Type: application/x-www-form-urlencoded
    27. r.POST("/form_getpostmap", func(c *gin.Context) {
    28. post_id := c.QueryMap("id")
    29. post_info := c.PostFormMap("user")
    30. c.JSON(http.StatusOK, gin.H{"code": 200, "id": post_id, "post": post_info})
    31. })
    32. // 示例4.获取URL路由中的参数
    33. // 此 handler 将匹配 /router_get/john (但不会匹配 /router_get/ 或者 /router_get)
    34. r.GET("/router_get/:name", func(c *gin.Context) {
    35. name := c.Param("name")
    36. c.String(http.StatusOK, "Hello %s", name)
    37. })
    38. // 此 handler 将匹配 /router_get/john/ 和 /router_get/john/send (若如果没有其他路由匹配 /router_get/john,它将重定向到 /router_get/john/)
    39. r.GET("/router_get/:name/*action", func(c *gin.Context) {
    40. name := c.Param("name")
    41. action := c.Param("action")
    42. message := name + " is " + action
    43. c.String(http.StatusOK, message)
    44. })
    45. // 示例6.c.ShouldBind 函数:针对提交URL参数进行自动选择合适的绑定。
    46. //GET /router_get/login?user=weiyigeek&password=123456
    47. r.GET("/router_get/login", func(c *gin.Context) {
    48. // 你可以使用显式绑定声明绑定 Query Param:
    49. // c.ShouldBindWith(&form, binding.Query)
    50. // 或者简单地使用 ShouldBind 方法自动绑定:
    51. // POST请求:登录表单的结构体
    52. type LoginForm struct {
    53. User string `form:"user" binding:"required"`
    54. Password string `form:"password" binding:"required"`
    55. }
    56. var login LoginForm
    57. // 在这种情况下,将自动选择合适的绑定 (值得学习。)
    58. if c.ShouldBind(&login) == nil {
    59. if login.User == "user" && login.Password == "password" {
    60. c.JSON(http.StatusOK, gin.H{"code": 200, "message": "you are logged in"})
    61. } else {
    62. c.JSON(http.StatusUnauthorized, gin.H{"code": 401, "stmessageatus": "unauthorized"})
    63. }
    64. }
    65. })
    66. // 示例6.c.ShouldBind 函数:针对提交数据表单进行自动选择合适的绑定。
    67. // POST /router_get/login
    68. // ......
    69. // user=weiyigeek&password=123456
    70. r.POST("/login", func(c *gin.Context) {
    71. // 简单地使用 ShouldBind 方法自动绑定:
    72. // POST请求:登录表单的结构体
    73. type LoginForm struct {
    74. User string `form:"user" binding:"required"`
    75. Password string `form:"password" binding:"required"`
    76. }
    77. var login LoginForm
    78. // 在这种情况下,将自动选择合适的绑定 (值得学习。)
    79. if c.ShouldBind(&login) == nil {
    80. if login.User == "weiyigeek" && login.Password == "123456" {
    81. c.JSON(http.StatusOK, gin.H{"code": 200, "message": "you are logged in"})
    82. } else {
    83. c.JSON(http.StatusUnauthorized, gin.H{"code": 401, "stmessageatus": "unauthorized"})
    84. }
    85. }
    86. })
    87. // 示例7.c.ShouldBind 函数绑定 HTML 复选框表单数据
    88. //
    89. //

      Check some colors

    90. //
    91. //
    92. //
    93. //
    94. //
    95. //
    96. //
    97. //
    98. r.POST("/mutil_color", func(c *gin.Context) {
    99. // 绑定 HTML 复选框
    100. type myForm struct {
    101. Colors []string `form:"colors[]"`
    102. }
    103. var fakeForm myForm
    104. // 解析 form 表单
    105. if c.ShouldBind(&fakeForm) == nil {
    106. // 输出结果: {"code": 200,"color":["red","green","blue"]}
    107. c.JSON(http.StatusOK, gin.H{"code": 200, "color": fakeForm.Colors})
    108. }
    109. })
    110. return r
    111. }
    112. func main() {
    113. r := setupRouter()
    114. // Listen and Server in 0.0.0.0:8080
    115. r.Run(":8080")
    116. }

    运行结果:

    1. [GIN-debug] GET /param_get --> main.setupRouter.func1 (3 handlers)
    2. [GIN-debug] POST /form_post --> main.setupRouter.func2 (3 handlers)
    3. [GIN-debug] POST /form_getpostmap --> main.setupRouter.func3 (3 handlers)
    4. [GIN-debug] GET /router_get/:name --> main.setupRouter.func4 (3 handlers)
    5. [GIN-debug] GET /router_get/:name/*action --> main.setupRouter.func5 (3 handlers)
    6. [GIN-debug] GET /router_get/login --> main.setupRouter.func6 (3 handlers)
    7. [GIN-debug] POST /login --> main.setupRouter.func7 (3 handlers)
    8. [GIN-debug] POST /mutil_color --> main.setupRouter.func8 (3 handlers)

    执行结果

    1. # 示例1
    2. curl --location 'http://127.0.0.1:8080/param_get?firstname=Weiyi&lastname=Geek'
    3. # Hello Weiyi Geek, Welcome To Study Go Gin!
    4. # 示例2
    5. curl --location 'http://127.0.0.1:8080/form_post' \
    6. --header 'Content-Type: application/x-www-form-urlencoded' \
    7. --data-urlencode 'name=WeiyiGeek' \
    8. --data-urlencode 'message=一个想成为全栈的男人'
    9. # {"code":200,"data":{"message":"一个想成为全栈的男人","name":"WeiyiGeek"}}
    10. # 示例3
    11. curl --location --globoff 'http://127.0.0.1:8080/form_getpostmap?id[name]=weiyigeek&id[site]=www.weiyigeel.top' \
    12. --header 'Content-Type: application/x-www-form-urlencoded' \
    13. --data 'user[name]=weiyigeek&user[message]=一个想成为全栈的男人'
    14. # {"code":200,"id":{"name":"weiyigeek","site":"www.weiyigeel.top"},"post":{"message":"一个想成为全栈的男人","name":"weiyigeek"}}
    15. # 示例4
    16. curl --location 'http://127.0.0.1:8080/router_get/weiyigeek'
    17. # Hello weiyigeek
    18. # 示例5
    19. curl --location 'http://127.0.0.1:8080/router_get/weiyigeek/devops'
    20. # weiyigeek is /devops
    21. # 示例6 & 示例7
    22. curl --location 'http://127.0.0.1:8080/router_get/login?user=user&password=password'
    23. curl --location 'http://127.0.0.1:8080/login' \
    24. --header 'Content-Type: application/x-www-form-urlencoded' \
    25. --data 'user=user&password=password'
    26. # {"code":200,"message":"you are logged in"}

    5.Cookie、Header 获取和设置

    描述: 在 Gin 中我们可以使用 c.Cookie 和 c.SetCookie方法 以及 c.GetHeader 和 c.Header 方法快速的获取设置 Cookie与Header值。

    代码示例:

    1. package main
    2. import (
    3. "net/http"
    4. "github.com/gin-gonic/gin"
    5. )
    6. // 示例1.设置与获取 Cookie Handler
    7. func getCookieHandler(c *gin.Context) {
    8. // 获取 cookies 中 secret 字段信息
    9. secret, err := c.Cookie("secret")
    10. // 判断 获取cookies 是否有误
    11. if err == nil {
    12. // 设置 Cookies:SetCookie(name string, value string, maxAge int, path string, domain string, secure bool, httpOnly bool)
    13. c.SetCookie("gin_cookie", "test", 3600, "/", "blog.weiyigeek.top", false, true)
    14. c.JSON(http.StatusOK, gin.H{
    15. "code": 200,
    16. "data": gin.H{
    17. "secret": secret,
    18. },
    19. })
    20. } else {
    21. c.JSON(http.StatusBadRequest, gin.H{
    22. "code": 0,
    23. "message": "请求参数有误!",
    24. })
    25. }
    26. }
    27. // 示例2.设置与获取 header Handler
    28. func getHeaderHandler(c *gin.Context) {
    29. // 获取 header 中 secret 字段信息
    30. secret := c.GetHeader("secret")
    31. // 判断 获取 header 是否为空
    32. if secret != "" {
    33. // 设置 header (key string, value string)
    34. c.Header("gin_header", "blog.weiyigeek.top")
    35. c.JSON(http.StatusOK, gin.H{
    36. "code": 200,
    37. "data": gin.H{
    38. "secret": secret,
    39. },
    40. })
    41. } else {
    42. c.JSON(http.StatusBadRequest, gin.H{
    43. "code": 0,
    44. "message": "请求参数有误!",
    45. })
    46. }
    47. }
    48. func main() {
    49. // Default返回一个Engine实例,该实例已连接Logger和Recovery中间件。
    50. r := gin.Default()
    51. // 示例1.设置与获取 Cookie
    52. r.GET("/cookie_get", getCookieHandler)
    53. // 示例2.设置与获取 Header
    54. r.GET("/header_get", getHeaderHandler)
    55. // Listen and Server in 0.0.0.0:8080
    56. r.Run(":8080")
    57. }

    执行结果:

    1. # Cookie 获取与设置
    2. curl --location 'http://10.20.172.106:8080/cookie_get' \
    3. --header 'Cookie: secret=weiyigeek.top;'
    4. # Header 获取与设置
    5. curl --location 'http://10.20.172.106:8080/header_get' \
    6. --header 'secret: weiyigeek.top'

    5f64627912eeb6330f58b90fa7acf7ab.png

    偷偷的告诉你哟?极客全栈修炼】微信小程序已经上线了,

    可直接在微信里面直接浏览博主博客了哟,后续将上线更多有趣的小工具。


    6.路由组及模型绑定和验证

    描述: 在Gin中若要将请求体绑定到结构体中可使用模型绑定,其目前支持JSON、XML、YAML和标准表单值的绑定(foo=bar&boo=baz),但是使用时,必须要在要绑定的所有字段上,设置相应的tag。

    例如,使用 JSON 绑定时,设置字段标签为 json:"参数名称",如果一个字段的 tag 加上了 binding:"required",但绑定时是空值, Gin 会报错。

    Gin提供了两类绑定方法, 在使用 Bind 方法时,Gin 会尝试根据 Content-Type 推断如何绑定。
    Must bind

    Methods - Bind, BindJSON, BindXML, BindQuery, BindYAML
    说明: 上述方法属于 MustBindWith 的具体调用,如果发生绑定错误则请求终止,并触发 c.AbortWithError(400, err).SetType(ErrorTypeBind)。

    Should bind

    Methods - ShouldBind, ShouldBindJSON, ShouldBindXML, ShouldBindQuery, ShouldBindYAML
    说明: Behavior - 这些方法属于 ShouldBindWith 的具体调用,如果发生绑定错误,Gin 会返回错误并由开发者处理错误和请求。

    代码示例

    1. package main
    2. import (
    3. "net/http"
    4. "github.com/gin-gonic/gin"
    5. "github.com/gin-gonic/gin/binding"
    6. )
    7. // 绑定 FORM / JSON / XML
    8. type Login struct {
    9. User string `form:"user" json:"user" xml:"user" binding:"required"`
    10. Password string `form:"password" json:"password" xml:"password" binding:"required"`
    11. // Birthday time.Time `form:"birthday" json:"birthday" xml:"birthday" time_format:"2006-01-02" time_utc:"1"`
    12. }
    13. // 你可以使用显式绑定声明绑定 Uri:
    14. // c.ShouldBindWith(&uri, binding.Uri)
    15. // 使用 ShouldBindUri 函数绑定 Uri 路由参数
    16. func bindUri(c *gin.Context) {
    17. // 自定义结构体进行数据绑定
    18. type Person struct {
    19. ID string `uri:"uuid" binding:"required"`
    20. Name string `uri:"name" binding:"required"`
    21. }
    22. var person Person
    23. // 绑定路由参数
    24. if err := c.ShouldBindUri(&person); err != nil {
    25. c.JSON(http.StatusBadRequest, gin.H{"msg": err.Error()})
    26. return
    27. }
    28. c.JSON(http.StatusOK, gin.H{"name": person.Name, "uuid": person.ID})
    29. }
    30. // 使用 ShouldBindQuery 函数只绑定 url 查询参数而忽略 post 数据
    31. func bindQuery(c *gin.Context) {
    32. // GET请求:url 查询参数的结构体
    33. type BlogUrl struct {
    34. Name string `form:"name" binding:"required"`
    35. Blog string `form:"blog" binding:"required"`
    36. }
    37. var site BlogUrl
    38. // 绑定 URL 请求参数
    39. if c.ShouldBindQuery(&site) == nil {
    40. c.JSON(http.StatusOK, gin.H{"code": 200, "data": gin.H{"name": site.Name, "blog": site.Blog}})
    41. } else {
    42. c.JSON(http.StatusOK, gin.H{"code": 0, "message": "Query Param Error!"})
    43. }
    44. }
    45. // 使用 ShouldBindJSON 绑定 JSON 数据
    46. func bindJson(c *gin.Context) {
    47. var json Login
    48. if err := c.ShouldBindJSON(&json); err != nil {
    49. c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
    50. return
    51. }
    52. if json.User != "weiyigeek" || json.Password != "123456" {
    53. c.JSON(http.StatusUnauthorized, gin.H{"code": 0, "status": "unauthorized"})
    54. return
    55. }
    56. c.JSON(http.StatusOK, gin.H{"code": 200, "status": "you are logged in"})
    57. }
    58. // 使用 ShouldBindXML 绑定 xml 数据
    59. func bindXml(c *gin.Context) {
    60. var xml Login
    61. if err := c.ShouldBindXML(&xml); err != nil {
    62. c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
    63. return
    64. }
    65. if xml.User != "weiyigeek" || xml.Password != "123456" {
    66. c.JSON(http.StatusUnauthorized, gin.H{"code": 0, "status": "unauthorized"})
    67. return
    68. }
    69. c.JSON(http.StatusOK, gin.H{"code": 200, "status": "you are logged in"})
    70. }
    71. // 使用 ShouldBindForm 绑定 表单 数据
    72. func bindForm(c *gin.Context) {
    73. var form Login
    74. if err := c.ShouldBindWith(&form, binding.Form); err != nil {
    75. c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
    76. return
    77. }
    78. if form.User != "weiyigeek" || form.Password != "123456" {
    79. c.JSON(http.StatusUnauthorized, gin.H{"code": 0, "status": "unauthorized"})
    80. return
    81. }
    82. c.JSON(http.StatusOK, gin.H{"code": 200, "status": "you are logged in"})
    83. }
    84. // 绑定表单数据至自定义结构体
    85. type StructA struct {
    86. Name string `form:"name" json:"name" xml:"name" text:"name"`
    87. }
    88. type StructB struct {
    89. // 方式1
    90. NestedStruct StructA
    91. // 方式2
    92. // NestedStructPointer *StructA
    93. // 方式3
    94. // NestedAnonyStruct struct {
    95. // FieldX string `form:"field_x"`
    96. // }
    97. Blog string `form:"blog" json:"blog" xml:"blog" text:"name"`
    98. }
    99. func bindCustom(c *gin.Context) {
    100. var person StructB
    101. if c.Bind(&person) == nil {
    102. c.JSON(http.StatusOK, gin.H{
    103. "person": person.NestedStruct,
    104. "blog": person.Blog,
    105. })
    106. } else {
    107. c.JSON(http.StatusBadRequest, gin.H{
    108. "message": "BadRequest",
    109. })
    110. }
    111. }
    112. func main() {
    113. // Default返回一个Engine实例,该实例已连接Logger和Recovery中间件。
    114. r := gin.Default()
    115. // 简单的路由组: v1
    116. v1 := r.Group("/v1")
    117. {
    118. // 示例1.使用 ShouldBindUri 函数绑定 Uri 路由参数
    119. v1.GET("/person/:name/:uuid", bindUri)
    120. // 示例2.使用 ShouldBindQuery 函数只绑定 url 查询参数而忽略 post 数据
    121. v1.Any("/site", bindQuery)
    122. }
    123. // 简单的路由组: v2
    124. v2 := r.Group("/v2")
    125. {
    126. // 示例3.使用 ShouldBindJSON 绑定 JSON 数据
    127. v2.POST("/loginJSON", bindJson)
    128. // 示例4.使用 ShouldBindXML 绑定 xml 数据
    129. v2.POST("/loginXML", bindXml)
    130. // 示例5.使用 ShouldBindForm 绑定 表单 数据
    131. v2.POST("/loginFORM", bindForm)
    132. }
    133. // 简单的路由组: v3
    134. v3 := r.Group("/v3")
    135. {
    136. v3.POST("/person", bindCustom)
    137. }
    138. // Listen and Server in 0.0.0.0:8080
    139. r.Run(":8080")
    140. }

    运行结果:

    1. [GIN-debug] GET /v1/person/:name/:uuid --> main.bindUri (3 handlers)
    2. [GIN-debug] GET /v1/site --> main.bindQuery (3 handlers)
    3. [GIN-debug] POST /v1/site --> main.bindQuery (3 handlers)
    4. [GIN-debug] PUT /v1/site --> main.bindQuery (3 handlers)
    5. [GIN-debug] PATCH /v1/site --> main.bindQuery (3 handlers)
    6. [GIN-debug] HEAD /v1/site --> main.bindQuery (3 handlers)
    7. [GIN-debug] OPTIONS /v1/site --> main.bindQuery (3 handlers)
    8. [GIN-debug] DELETE /v1/site --> main.bindQuery (3 handlers)
    9. [GIN-debug] CONNECT /v1/site --> main.bindQuery (3 handlers)
    10. [GIN-debug] TRACE /v1/site --> main.bindQuery (3 handlers)
    11. [GIN-debug] POST /v2/loginJSON --> main.bindJson (3 handlers)
    12. [GIN-debug] POST /v2/loginXML --> main.bindXml (3 handlers)
    13. [GIN-debug] POST /v2/loginFORM --> main.bindForm (3 handlers)
    14. [GIN-debug] POST /v3/person --> main.bindCustom (3 handlers)

    访问结果:

    1. # 示例1
    2. http://10.20.172.106:8080/v1/person/weiyigeek/d932949f-6653-419b-977f-57e1ec1ec52d
    3. # {"name":"weiyigeek","uuid":"d932949f-6653-419b-977f-57e1ec1ec52d"}
    4. # 示例2
    5. http://10.20.172.106:8080/v1/site?name=weiyigeek&blog=blog.weiyigeek.top
    6. curl --location --request POST 'http://10.20.172.106:8080/v1/site?name=weiyigeek&blog=blog.weiyigeek.top' \
    7. --header 'Content-Type: application/x-www-form-urlencoded'
    8. # {"code":200,"data":{"blog":"blog.weiyigeek.top","name":"weiyigeek"}}
    9. # 示例3.Content-Type: application/json
    10. curl --location 'http://10.20.172.106:8080/v2/loginJSON' \
    11. --header 'Content-Type: application/json' \
    12. --data '{"user":"weiyigeek","password":"123456"}'
    13. # 示例4.Content-Type: application/xml
    14. curl --location 'http://10.20.172.106:8080/v2/loginXML' \
    15. --header 'Content-Type: application/xml' \
    16. --data '
    17. weiyigeek
    18. 123
    19. '
    20. # 示例5.application/x-www-form-urlencoded
    21. curl --location 'http://10.20.172.106:8080/v2/loginFORM' \
    22. --header 'Content-Type: application/x-www-form-urlencoded' \
    23. --data-urlencode 'user=weiyigeek' \
    24. --data-urlencode 'password=123456'
    25. # 示例6.
    26. curl --location 'http://10.20.172.106:8080/v3/person' \
    27. --form 'name="WeiyiGeek"' \
    28. --form 'blog="blog.weiyigeek.top"'
    29. # {"blog":"blog.weiyigeek.top","person":{"name":"WeiyiGeek"}}

    3d32c41f1c9bf118a1178fedc044be68.png

    补充示例: 将 request body 绑定到不同的结构体中, 此处需要注意使用 c.ShouldBindBodyWith 会在绑定之前将 body 存储到上下文中, 其对性能造成轻微影响,如果调用一次就能完成绑定的话,建议不要用这个方法而是使用c.ShouldBind方法。

    1. type formA struct {
    2. Foo string `json:"foo" xml:"foo" binding:"required"`
    3. }
    4. type formB struct {
    5. Bar string `json:"bar" xml:"bar" binding:"required"`
    6. }
    7. func SomeHandler(c *gin.Context) {
    8. objA := formA{}
    9. objB := formB{}
    10. // 读取 c.Request.Body 并将结果存入上下文。
    11. if errA := c.ShouldBindBodyWith(&objA, binding.JSON); errA == nil {
    12. c.String(http.StatusOK, `the body should be formA`)
    13. // 这时, 复用存储在上下文中的 body。
    14. } else if errB := c.ShouldBindBodyWith(&objB, binding.JSON); errB == nil {
    15. c.String(http.StatusOK, `the body should be formB JSON`)
    16. // 可以接受其他格式
    17. } else if errB2 := c.ShouldBindBodyWith(&objB, binding.XML); errB2 == nil {
    18. c.String(http.StatusOK, `the body should be formB XML`)
    19. } else {
    20. ...
    21. }
    22. }

    7.HTTP重定向的几种方式

    描述: 在Gin中要实现重定向是很容易内部、外部重定向均支持,通常情况下有三种重定向,一种是 301 重定向,另一种是 302 重定向,最后一种是路由重定向(即访问的路由地址不会发生改变,请求内部其路由返回数据的,有点反代的感觉)

    代码示例:

    1. package main
    2. import (
    3. "github.com/gin-gonic/gin"
    4. "log"
    5. "net/http"
    6. )
    7. func main() {
    8. gin.SetMode(gin.DebugMode)
    9. g := gin.Default()
    10. // HTTP 重定向很容易
    11. // 通过 GET 方法进行 HTTP 301 重定向
    12. g.GET("/redirect-test-1", func(c *gin.Context) {
    13. c.Redirect(http.StatusMovedPermanently, "http://www.weiyigeek.top/")
    14. })
    15. // GET 方法进行 HTTP 302 重定向
    16. g.POST("/redirect-test-2", func(c *gin.Context) {
    17. c.Redirect(http.StatusFound, "/test2")
    18. })
    19. // 通过使用 HandleContext 进行路由重定向
    20. g.GET("/test1", func(c *gin.Context) {
    21. c.Request.URL.Path = "/test2"
    22. g.HandleContext(c) // 返回的是 /test2 路由的数据
    23. })
    24. g.GET("/test2", func(c *gin.Context) {
    25. c.JSON(200, gin.H{"hello": "world"})
    26. })
    27. // 默认监听并在 0.0.0.0:8080 上启动服务
    28. g.Run()
    29. }

    执行效果:

    1. curl -i http://10.20.172.106:8080/redirect-test-1
    2. curl -X POST -i http://10.20.172.106:8080/redirect-test-2
    3. curl -i http://10.20.172.106:8080/test1

    c1b130f2aec0d4cd04d5a2bd85b2ae82.png

    亲,文章就要看完了,不关注一下【全栈工程师修炼指南】吗?

    1c7e0b5605659ad523bfcdec41f259a3.jpeg

    8.外部请求转发及数据验证

    描述: 我们可以通过DataFromReader方法将指定的读取器写入主体流并更新HTTP代码,并返回给客户端,其次在提交数据时可以针对数据类型进行验证。

    示例代码:

    1. package main
    2. import (
    3. "fmt"
    4. "net/http"
    5. "time"
    6. "github.com/gin-gonic/gin"
    7. "github.com/gin-gonic/gin/binding"
    8. "github.com/go-playground/validator"
    9. )
    10. // 示例1.SomeDataFromReader 将外部URL请求响应给指定用户
    11. func getSomeDataFromReader(c *gin.Context) {
    12. // GET 请求外部地址
    13. response, err := http.Get("https://blog.weiyigeek.top/img/avatar.jpg")
    14. if err != nil || response.StatusCode != http.StatusOK {
    15. c.Status(http.StatusServiceUnavailable)
    16. return
    17. }
    18. // 外部地址请求响应结果
    19. reader := response.Body
    20. contentLength := response.ContentLength
    21. contentType := response.Header.Get("Content-Type")
    22. extraHeaders := map[string]string{
    23. "Content-Disposition": `attachment; filename="avatar.jpg"`,
    24. }
    25. // DataFromReader 将指定的读取器写入主体流并更新HTTP代码,并返回给客户端
    26. // DataFromReader(code int, contentLength int64, contentType string, reader io.Reader, extraHeaders map[string]string)
    27. c.DataFromReader(http.StatusOK, contentLength, contentType, reader, extraHeaders)
    28. }
    29. // 示例2. Validator Handler 自定义验证器
    30. // User 结构体包含绑定和验证的数据。
    31. type User struct {
    32. Name string `json:"name"`
    33. Phone string `json:"phone"`
    34. Birth time.Time `json:"birth" binding:"required" time_format:"2006-01-02"`
    35. Login time.Time `json:"login" binding:"required" time_format:"2006-01-02"`
    36. }
    37. // 验证用户输入基础信息是否为空
    38. func UserStructLevelValidation(sl validator.StructLevel) {
    39. user := sl.Current().Interface().(User)
    40. if len(user.Name) == 0 && len(user.Phone) == 0 {
    41. sl.ReportError(user.Name, "Name", "name", "fnameorlname", "")
    42. sl.ReportError(user.Phone, "Phone", "phone", "fnameorlphone", "")
    43. }
    44. }
    45. // 验证用户输入日期是否有误
    46. var userDateValida validator.Func = func(fl validator.FieldLevel) bool {
    47. // 使用了反射机制
    48. date, ok := fl.Field().Interface().(time.Time)
    49. fmt.Println(date)
    50. if ok {
    51. // 判断输入的时间是否在当前时间之后
    52. today := time.Now()
    53. if today.After(date) {
    54. return false
    55. }
    56. }
    57. return true
    58. }
    59. func postValidatorHandler(c *gin.Context) {
    60. // 实例化结构体对象,解析并绑定对应数据
    61. var user User
    62. if err := c.ShouldBindWith(&user, binding.JSON); err == nil {
    63. c.JSON(http.StatusOK, gin.H{"code": "200", "data": gin.H{"name": user.Name, "phone": user.Phone, "birthday": user.Birth, "logintime": user.Login}})
    64. } else {
    65. c.JSON(http.StatusBadRequest, gin.H{"code": "0", "message": err.Error()})
    66. }
    67. }
    68. func main() {
    69. // Default返回一个Engine实例,该实例已连接Logger和Recovery中间件。
    70. r := gin.Default()
    71. // 示例1.SomeDataFromReader 将外部URL请求响应给指定用户
    72. r.GET("/someDataFromReader", getSomeDataFromReader)
    73. if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
    74. v.RegisterStructValidation(UserStructLevelValidation, User{})
    75. //v.RegisterValidation("datevalida", userDateValida)
    76. }
    77. // 示例2.Validator Handler 自定义验证器 Header
    78. r.POST("/ValidatorHandler", postValidatorHandler)
    79. // Listen and Server in 0.0.0.0:8080
    80. r.Run(":8080")
    81. }

    执行结果:

    6cbb1bdb8054e765f1d148c73c60ca6a.png


    0x0n 入坑出坑

    错误1.在VScode中无法安装Go插件中相关依赖模块

    解决办法:

    1. Tools environment: GOPATH=D:\Study\Go\package
    2. Installing 7 tools at D:\Study\Go\package\bin in module mode.
    3. gotests
    4. gomodifytags
    5. impl
    6. goplay
    7. dlv
    8. staticcheck
    9. gopls

    错误2.在Windows10中使用rotatelogs模块时创建软连接报A required privilege is not held by the client.错误。

    错误信息: failed to rotate: failed to create new symlink: symlink \system.log.20230214.log s/system.log.20230214.log_symlink: A required privilege is not held by the client.
    问题原因: 命令行终端问题或者未开启Windows10系统开发人员选项。
    解决办法:

    1. # 1.提升执行程序命令终端权限
    2. - 在Windows10桌面,右键点击桌面左下角的开始按钮 ,在弹出的菜单中选择CMD “命令提示符 (管理员)”一项
    3. - 在PowerShell中执行 `Start-Process -verb runas "C:\Windows\System32\cmd.exe" 命令
    4. # 2.开启Windows10系统开发人员选项
    5. - 点击【开始】-> 搜索开发者模式 -> 打开开发人员模式

    828e78f9d76706f6ad07144886bb6b6f.png

    本文至此完毕,更多技术文章,尽情等待下篇好文!

    原文地址: https://blog.weiyigeek.top/2020/4-23-602.html

    如果此篇文章对你有帮助,请你将它分享给更多的人! 

    ed708c5b461224005bbe4de7b5653987.gif

    469cad09f86073e89794b1274bdf36b9.png 学习书籍推荐 往期发布文章 6e92d0bff32dabf570ca8334deb75ad3.png

    公众号回复【0008】获取【Ubuntu22.04安装与加固脚本】

    公众号回复【10001】获取【WinServer安全加固脚本】

    公众号回复【10002】获取【KylinOS银河麒麟安全加固脚本】

    公众号回复【0011】获取【k8S二进制安装部署教程】

    公众号回复【0014】获取【Nginx学习之路汇总】

    公众号回复【0015】获取【Jenkins学习之路汇总】

    公众号回复【10005】获取【adb工具刷抖音赚米】

     

    欢迎关注 【全栈工程师修炼指南】(^U^)ノ~YO

    添加作者微信【weiyigeeker 】 一起学习交流吧!

    关注回复【学习交流群】即可加入【安全运维沟通交流小群

    温馨提示: 由于作者水平有限,本章错漏缺点在所难免,希望读者批评指正,若有问题或建议请在文章末尾留下您宝贵的经验知识,或联系邮箱地址

    master@weiyigeek.top 或 关注公众号 [全栈工程师修炼指南] 留言。

    点个【赞 + 在看】吧!

    点击【"阅读原文"】获取更多有趣的知识!   

  • 相关阅读:
    非对称加密、解密原理及openssl中的RSA示例代码
    互融云农产品追溯系统:区块链存证技术实现双向可追溯
    7 矩阵中战斗力最弱的 K 行
    webGL学习
    责任链模式
    第四章《类与对象》第5节:this关键字及其用法
    MATLB|多微电网及分布式能源交易
    【雷达通信】合成孔径雷达地面运动目标检测技术研究(Matlab代码实现)
    redis中集合的相关命令
    异常是怎么被处理的?这题的答案不在源码里面。
  • 原文地址:https://blog.csdn.net/u013072756/article/details/131148973