前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >gin学习笔记

gin学习笔记

作者头像
用户2825413
发布2020-02-20 13:49:51
7350
发布2020-02-20 13:49:51
举报
使用 go mod 下载依赖

首先我们使用 go moudle来管理依赖, go版本要求在 1.11.1 及以上

代码语言:javascript
复制
export GO111MODULE=on

export GOPROXY=https://mirrors.aliyun.com/goproxy/

初始化 mod 配置

代码语言:javascript
复制
go mod init

>在当前目录下会生成 go.mod 文件, 作为依赖配置.

build项目后会自动下载依赖

其中在实际项目中发现 goland 无法识别项目代码, 需要 在 goland 的 setting 里设置启用 Go Modules

代码语言:javascript
复制
goland Preference -> Go -> Go Modules(vgo)
-> Enable Go Modules(vgo)intergration

开始以第一个项目

代码语言:javascript
复制
package main

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

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

	// get 方式请求
	r.GET("/get", func(c *gin.Context) {
		c.JSON(200, "get")
	})

	// post 方式请求
	r.POST("/post", func(c *gin.Context) {
		c.JSON(200, "post")
	})

	// 任意类型请求
	r.Any("any", func(c *gin.Context) {
		c.JSON(200, "any")
	})

	// handle 指定方式请求
	r.Handle("POST", "/handle_post", func(c *gin.Context) {
		c.JSON(200, "handle post")
	})
   
        //r.Run(":8888") 监听 8888 端口
        r.Run()
}

这里我们定义了常用的 get post方法, 通过 debug 可以看到 any 方式为我们创建了所有的可以使用的请求的方式, 最后使用了 handle 方式指定方法参数. gin.Default 配置默认的参数, Run 启动 http 服务, 默认监听 8080 端口

代码语言:javascript
复制
curl -XPOST http://127.0.0.1:8080/post
output:
    "post"

curl -XPOST http://127.0.0.1:8080/handle_post
output:
   "handle post"

gin 的路由同请求方式下不能重复定义, 否则启动将会异常退出

获取请求参数

代码语言:javascript
复制
package main

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

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

	// get 方式请求
	r.GET("/getUser/:uid", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"uid": c.Param("uid"),
		})
	})

	r.Run()
}

output:

代码语言:javascript
复制
curl http://127.0.0.1:8080/getUser/1451
output:
   {"uid":"1451"}

http.StatusOK 引用的 net/http 包中的状态码定义, c.Param() 获取 uid 的值

前缀匹配

在工作中我们希望路由匹配到某个前缀

代码语言:javascript
复制
r.GET("/user/*a", func(c *gin.Context) {
	c.String(http.StatusOK, "hello user")
})

这样只要请求到 user 就可以拦截到了, 注意在 gin 里是不允许再设置类似 /user/admin 这样的路由了. 这里是没有路由优先级的.

请求参数默认值

代码语言:javascript
复制
r.GET("/user", func(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H{
    	"name": c.Query("name"),
    	"sex":  c.DefaultQuery("sex", "女"),
    })
})

请求示例

代码语言:javascript
复制
curl http://127.0.0.1:8080/user?name=1
output:
    {"name":"1","sex":"女"}

获取post方式请求的参数

代码语言:javascript
复制
c.String(http.StatusOK, c.PostForm("name"))
//获取name值, 默认值为小明
c.String(http.StatusOK, c.DefaultPostForm("name", "小明")
请求的json获取
代码语言:javascript
复制
r.POST("/user", func(c *gin.Context) {
	bodyBytes, error := ioutil.ReadAll(c.Request.Body)
	if error != nil {
		c.String(http.StatusOK, error.Error())
		c.Abort()
	}

	c.String(http.StatusOK, string(bodyBytes))
})
代码语言:javascript
复制
curl -XPOST http://127.0.0.1:8080/user?name=1 -d {"name":1}
output:
  {name:1}

但是通过 readAll 读取后再获取值是获取不到的. 解决方案就是再将值写回去.

如何将获取到的json绑定到结构体上
代码语言:javascript
复制
package main

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

type requestUser struct {
	Name string `form:"name"`
	Uid  int    `form:"uid"`
	Age  int    `form:"age"`
	Date time.Time `form:"date"`
}

func main() {
	r := gin.Default()
	r.POST("/user", userAction)
	r.Run()
}

func userAction(c *gin.Context) {
	var requestUser requestUser
    
        //这里是根据请求的content-type进行解析(可以解析json)
	if err := c.ShouldBind(&requestUser); err != nil {
		c.String(http.StatusOK, err.Error())
		c.Abort()
	}

	c.String(http.StatusOK, "%v", requestUser)
}
代码语言:javascript
复制
curl -XPOST http://127.0.0.1:8080/user -d 'name=xiaoming&age=23'
output:
    {xiaoming 0 23}

curl -XPOST http://127.0.0.1:8080/user -d 'name=xiaoming&date=2019-01-01'
output:
    parsing time "2019-01-01" as "2006-01-02T15:04:05Z07:00": cannot parse "" as "T"{xiaoming 0 0 0001-01-01 00:00:00 +0000 UTC}

注意我们在传递时间格式的时候解析发生错误.

这里我们需要定义时间格式结构体解决

代码语言:javascript
复制
type requestUser struct {
	Name string `form:"name"`
	Uid  int    `form:"uid"`
	Age  int    `form:"age"`
	Date time.Time `form:"date" time_format:"2006-01-02"`
}
验证
代码语言:javascript
复制
type requestUser struct {
	Name string `form:"name" binding:"required,len=10"`
	Uid  int    `form:"uid"`
	Age  int    `form:"age" binding:"required,gt=10"`
}
代码语言:javascript
复制
curl -H "Content-Type:application/json" -XPOST http://127.0.0.1:8080/user -d '{"name":"xiaoming234342342342342","age":11}'
output:
    Key: 'requestUser.Name' Error:Field validation for 'Name' failed on the 'len' tag{xiaoming234342342342342 0 11}

更多验证规则: https://godoc.org/gopkg.in/go-playground/validator.v8

###自定义 validator

代码语言:javascript
复制
type requestUser struct {
	Name string `form:"name" binding:"required,len=10"`
	Uid  int    `form:"uid" binding:"checkUidAvailable"`
	Age  int    `form:"age" binding:"required,gt=10"`
}

编写 checkUidAvailable

代码语言:javascript
复制
// Structure
func checkUidAvailable(field validator.FieldLevel) bool {
	if fieldValue, ok := field.Field().Interface().(int); ok {
		//随机概率
		if rand.Intn(2) == fieldValue {
			return true
		}
	}

	return false
}

在 main方法中绑定

代码语言:javascript
复制
func main() {
	r := gin.Default()

	r.POST("/user", userAction)

        //绑定
	if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
		v.RegisterValidation("checkUidAvailable", checkUidAvailable)
	}

	r.Run()
}

方法执行, ShouldBind 自动验证

代码语言:javascript
复制
func userAction(c *gin.Context) {
	var requestUser requestUser

	if err := c.ShouldBind(&requestUser); err != nil {
		c.String(http.StatusInternalServerError, err.Error())
		c.Abort()
		return
	}
	
	c.String(http.StatusOK, "%v", requestUser)
}

中间件

代码语言:javascript
复制
f, _ := os.Create("gin.log")
gin.DefaultWriter = io.MultiWriter(f)
gin.DefaultErrorWriter = io.MultiWriter(f)

r := gin.New()
r.Use(gin.Logger(), gin.Recovery())

上面我们使用了 gin.New 实例的方式声明, r.Use使用了两个中间件, 一个为日志, 另一个为遇见 panic 抛出异常最上层, 让程序正常运行.

代码语言:javascript
复制
f, _ := os.Create("gin.log")
gin.DefaultWriter = io.MultiWriter(f)
gin.DefaultErrorWriter = io.MultiWriter(f)

r := gin.Default()

默认日志是输出到控制台, 将日志和错误日志全部输出到 gin.log

自定义中间件
代码语言:javascript
复制
func ipAuthMiddleWare() gin.HandlerFunc {
    return func(c *gin.Context) {
    	ipList := []string{
    		"127.0.0.1",
    	}
    
    	flag := false
    	clientIp := c.ClientIP()
    	for _, host := range ipList {
    		if clientIp != host {
    			flag = true
    			break
    		}
    	}
    
    	if !flag {
    		c.String(http.StatusOK, "error")
    		c.Abort()
    	}
    }
}

---

r.Use(gin.Logger(), gin.Recovery(), ipAuthMiddleWare())

路由组

代码语言:javascript
复制
api := r.Group("/api")
{
	api.GET("user", userAction)
}

访问 127.0.0.1:8080/api/user 路由组中引入中间件

代码语言:javascript
复制
api := r.Group("/api").Use(ipAuthMiddleWare())
{
	api.GET("user", userAction)
}

同样使用 use 即可

附录

json处理: https://colobu.com/2017/06/21/json-tricks-in-Go/#%E4%B8%B4%E6%97%B6%E5%BF%BD%E7%95%A5struct%E7%A9%BA%E5%AD%97%E6%AE%B5

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-01-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 呆呆熊的技术路 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 开始以第一个项目
  • 获取请求参数
    • 前缀匹配
      • 请求的json获取
        • 如何将获取到的json绑定到结构体上
          • 验证
          • 中间件
            • 自定义中间件
            • 路由组
            • 附录
            相关产品与服务
            消息队列 TDMQ
            消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档