GO之Gin实战速学基础篇

课程链接

文章目录

开发工具:goland+postman
课程地址:https://www.bilibili.com/video/BV1Mi4y147fF?p=1

第一篇 系统设计

路由分组

v1 := router.Group("v1/topics")
v1.GET("/", topics)
v1.GET("/:topic_id",src.GetTopicDetail)

简单Dao层代码封装、使用中间件模拟 鉴权

类似于python的装饰器

func MustLoginin() gin.HandlerFunc {
    return func(c *gin.Context){
        if _,status := c.GetQuery("token");!status{
            c.String(http.StatusUnauthorized, "缺少token参数")
            // 不会往下走
            c.Abort()
        }else {
            c.Next()
        }
    }
}

// 简单的dao层封装
func GetTopicDetail(c *gin.Context)  {
    c.String(200,"获取yopic为 %s 的帖子列表", c.Query("topic_id"))
}

func GetTopicList(c *gin.Context)  {
    c.String(200,"获取用户名 %s 的帖子列表", c.Query("user_name"))
}

func NewTopicList(c *gin.Context)  {
    c.String(200,"新增帖子")
}

func DelTopicList(c *gin.Context)  {
    c.String(200,"删除帖子")
}
package main

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

func topic_id(c *gin.Context)  {
    c.String(http.StatusOK, "获取帖子id =  %s", c.Param("topic_id"))
}

func topics(c *gin.Context)  {
    if c.Query("username") == ""{
        c.String(200,"获取帖子列表")
    }else{
        c.String(200,"获取用户名 %s 的帖子列表", c.Query("username"))
    }
}

func main() {
    router := gin.Default()
    // 路由分组
    v1 := router.Group("v1/topics")
    v1.GET("/", topics)
    v1.GET("/:topic_id",src.GetTopicDetail)

    // 中间件限制登录
    v1.Use(src.MustLoginin())
    {
        v1.POST("/",src.NewTopicList)
        v1.DELETE("/:topic_id",src.DelTopicList)
    }
    router.Run(":5000")
}

05 6内置验证器的初步使用、POST参数绑定

在这里插入图片描述
postman测试

在这里插入图片描述

结构字段验证--validator.v9

结构体

type Topic struct {
    TopicID    int `json:"id" form:"id"`
    TopicTitle string `json:"title" form:"title" binding:"min=4,max=20"`
    TopicShortTitle string `json:"stitle" form:"stitle" binding:"required,nefield=TopicTitle"`
    UserIP string `json:"ip" form:"ip" binding:"ipv4"`
    TopicScore int `json:"score" form:"score" binding:"omitempty,gt=5"` //  //omitempty要么不传,传的话就要大于5
    TopicUrl string `json:"url" form:"url" binding:"omitempty" validate:"topicurl"`
}

自定义验证函数

package src

import (
    "fmt"
    "gopkg.in/go-playground/validator.v9"
    "regexp"
)

func TopicUrl(v validator.FieldLevel) bool {
    if _, ok := v.Top().Interface().(*Topic); ok {
        // 正则匹配
        if matched,_ := regexp.MatchString(`\w[4,10]$`,v.Field().String());matched {
            return true
        }
        fmt.Println(v.Field().String())
        //return str != "invalid"
    }
    return false
}
package main

import (
    "github.com/gin-gonic/gin"
    "github.com/gin-gonic/gin/binding"
    "gopkg.in/go-playground/validator.v9"
    "net/http"
    "tocpic/src"
)

func topic_id(c *gin.Context)  {
    c.String(http.StatusOK, "获取帖子id =  %s", c.Param("topic_id"))
}

func topics(c *gin.Context)  {
    if c.Query("username") == ""{
        c.String(200,"获取帖子列表")
    }else{
        c.String(200,"获取用户名 %s 的帖子列表", c.Query("username"))
    }
}

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

    if v, ok := binding.Validator.Engine().(*validator.Validate); ok {  //类型断言
        v.RegisterValidation("topicurl", src.TopicUrl) //注册调用tag和自定义验证器
    }
    v1 := router.Group("v1/topics")
    v1.GET("/", src.GetTopicList)
    v1.GET("/:topic_id",src.GetTopicDetail)

    // 中间件限制登录
    v1.Use(src.MustLoginin())
    {
        v1.POST("/",src.NewTopic)
        v1.DELETE("/:topic_id",src.DelTopicList)
    }
    router.Run(":5000")
}func main() {
    router := gin.Default()

    validate := validator.New()
    //自己定义topicurl标签以及与之对应的处理逻辑
    validate.RegisterValidation("topicurl", src.TopicUrl)
    //查看是否符合验证
    err := validate.Struct(src.Topic{})

    if err != nil {
        fmt.Println(err)
    }

    v1 := router.Group("v1/topics")
    v1.GET("/", src.GetTopicList)
    v1.GET("/:topic_id",src.GetTopicDetail)

    // 中间件限制登录
    v1.Use(src.MustLoginin())
    {
        v1.POST("/",src.NewTopic)
        v1.DELETE("/:topic_id",src.DelTopicList)
    }
    router.Run(":5000")
}

第二篇 ORM

08 9到底要不要用ORM、Gorm入手、执行原始SQL”

    db, err := gorm.Open("mysql", "root:123456@tcp(127.0.0.1:3306)/gin")
    if err != nil {
        fmt.Println(err)
    }
    rows, _ := db.Raw("select topic_id, topic_title from topic").Rows()
    for rows.Next() {
        var t_id int
        var t_title string
        rows.Scan(&t_id, &t_title)
        fmt.Println(t_id, t_title)
    }
    defer db.Close()

09 10 结合Model进行数据映射,查询基本要点

    db, err := gorm.Open("mysql", "root:123456@tcp(127.0.0.1:3306)/gin")
    if err != nil {
        fmt.Println(err)
    }
    // 打印sql执行过程
    db.LogMode(true)
    // 结构体
    var tcs []src.TopicClass
    //tc := &src.TopicClass{}

    // 查询出全部数据
    db.Find(&tcs)
    fmt.Println(tcs)
    // 条件筛选
    db.Where("class_name=?","2").Find(&tcs)
    fmt.Println(tcs)

    // 指定表名
    //db.Table("topic_class").First(&tc)
    //fmt.Println(tc)

    rows, _ := db.Raw("select topic_id, topic_title from topic").Rows()
    for rows.Next() {
        var t_id int
        var t_title string
        rows.Scan(&t_id, &t_title)
        fmt.Println(t_id, t_title)
    }
    defer db.Close()

10.结合Gin实现查询api

func GetTopicDetail(c *gin.Context) {
    db, err := gorm.Open("mysql", "root:123456@tcp(127.0.0.1:3306)/gin")
    if err != nil {
        fmt.Println(err)
    }
    defer db.Close()
    // 打印sql执行过程
    db.LogMode(true)
    tid := c.Param("topic_id")
    topics := Topics{}
    db.Find(&topics, tid)
    c.JSON(200, topics)
}

查询使用初始化db
src/TopicDao.go

package src

import (
    "fmt"
    _ "github.com/go-sql-driver/mysql"
    "github.com/jinzhu/gorm"
)

var (
    DBHelper *gorm.DB
    err error
)

// 初始化
func init()  {
    var err error
    DBHelper, err := gorm.Open("mysql", "root:123456@tcp(127.0.0.1:3306)/gin")
    if err != nil {
        fmt.Println(err)
    }
    // 打印sql执行过程
    DBHelper.LogMode(true)

}

src/TopicDao.go

func GetTopicDetail(c *gin.Context) {

    tid := c.Param("topic_id")
    topics := Topics{}
    DBHelper.Find(&topics, tid)
    c.JSON(200, topics)

    defer DBHelper.Close()
}

第三篇

Redis缓存、连接池

  • redis配置 src/MyRedis.go
package src

import (
"fmt"
"github.com/garyburd/redigo/redis"
    "time"
)

var RedisDefaultPool *redis.Pool

func newPool(addr string) *redis.Pool {
    return &redis.Pool{
        Dial: func() (redis.Conn, error) {
            return redis.Dial("tcp", addr)
        },
        TestOnBorrow:    nil,
        MaxIdle:         3,
        MaxActive:       0,
        IdleTimeout:     210* time.Second,
        Wait:            false,
        MaxConnLifetime: 0,
    }
}

func init(){
    RedisDefaultPool = newPool("127.0.0.1:6379")
}

func main() {
    c, err := redis.Dial("tcp", "127.0.0.1:6379")
    if err != nil {
        fmt.Println("Connect to redis error", err)
        return
    }
    defer c.Close()
}
  • 获取redis的值

    func main(){
        coon := src.RedisDefaultPool.Get()
        ret, err := redis.String(coon.Do("get","name"))
        if err != nil {
            log.Println(err)
            return
        }
        log.Println(ret)
    }
    

结合gin实现基本的redis缓存、缓存穿透简单处理”

缓存穿透的概念很简单,用户想要查询一个数据,发现redis内存数据库没有,也就是缓存没有命中,于是向持久层数据库查询。发现也没有,于是本次查询失败。当用户很多的时候,缓存都没有命中,于是都去请求了持久层数据库。这会给持久层数据库造成很大的压力,这时候就相当于出现了缓存穿透。

这里需要注意和缓存击穿的区别,缓存击穿,是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。

func GetTopicDetail(c *gin.Context) {

    tid := c.Param("topic_id")
    topics := Topics{}
    coon := RedisDefaultPool.Get()
    redisKey := "topic_" + tid
    defer coon.Close()

    ret, err := redis.Bytes(coon.Do("get", redisKey))
    if err != nil { // 缓存没有
        DBHelper.Find(&topics, tid)
        retData, _ := ffjson.Marshal(topics)
        // redis简单的缓存穿透
        if topics.TopicID ==0 {
            coon.Do("setex", redisKey, 20, retData)
        }else {
            coon.Do("setex", redisKey, 50, retData)
        }
        c.JSON(200, topics)
        log.Println("从数据库读取")
    } else { //有缓存
        ffjson.Unmarshal(ret, &topics)
        c.JSON(200, topics)
        log.Println("从Redis读取")
    }
    defer DBHelper.Close()
}

实现Redis缓存的装饰器模式

在这里插入图片描述

  • 将上面的dao层改为```
    func GetTopicDetail(c *gin.Context) {

      tid := c.Param("topic_id")
      topics := Topics{}
      DBHelper.Find(&topics, tid)
      // 获取的数据都为dbResult
      c.Set("dbResult", topics)
      }
  • 装饰器模式 实现Redis缓存```
    package src

    import (

      "fmt"
      "github.com/garyburd/redigo/redis"
      "github.com/gin-gonic/gin"
      "github.com/pquerna/ffjson/ffjson"
      "log"

    )

    // 缓存装饰器
    func CacheDecorator(h gin.HandlerFunc, param string, redKeyPattern string, empty interface{}) gin.HandlerFunc {

      return func(context *gin.Context) {
          // redis判断
          getID := context.Param(param)                 // 得到id
          redisKey := fmt.Sprintf(redKeyPattern, getID) //拼接redisKey
          coon := RedisDefaultPool.Get() //连接redis
          defer coon.Close()
          ret, err := redis.Bytes(coon.Do("get", redisKey))
          if err != nil { // 缓存没有
              h(context) //执行目标方法,从数据库得到dbResult
              dbResult, exists := context.Get("dbResult")
              if !exists{
                  dbResult = empty
              }
              retData, _ := ffjson.Marshal(dbResult)
              // 设置redis缓存
              coon.Do("setex", redisKey, 20, retData)
              context.JSON(200,dbResult)
          } else {
              ffjson.Unmarshal(ret, &empty)
              context.JSON(200, dbResult)
              log.Println("从Redis读取")
          }
          //defer coon.Close()
      }

    }

```

本文转自 https://blog.csdn.net/weixin_43746433/article/details/106153293,如有侵权,请联系删除。


部分资料来源于网络,版权属其原著者所有,只供学习交流之用。如有侵犯您的权益,请联系【公众号:码农印象】删除,可在下方评论,亦可邮件至ysluckly.520@qq.com。互动交流时请遵守宽容、换位思考的原则。

×

喜欢就点赞,疼爱就打赏

(function(){ var bp = document.createElement('script'); bp.src = '//push.zhanzhang.baidu.com/push.js'; var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();
休闲小游戏