Gin 学习条记

打印 上一主题 下一主题

主题 962|帖子 962|积分 2886

教程地址:https://www.bilibili.com/video/BV1FV4y1C72M?spm_id_from=333.788.videopod.sections&vd_source=707ec8983cc32e6e065d5496a7f79ee6
  
01-项目搭建



  • 各常用目录的说明:
    https://github.com/golang-standards/project-layout/blob/master/README_zh.md

02-优雅启停



  • 用gin启动web服务器
  1. package main
  2. import (
  3.         "context"
  4.         "github.com/gin-gonic/gin"
  5.         "log"
  6.         "net/http"
  7.         "os"
  8.         "os/signal"
  9.         "syscall"
  10.         "time"
  11. )
  12. func main() {
  13.         r := gin.Default()
  14.         srv := &http.Server{
  15.                 Addr:    ":5000",
  16.                 Handler: r,
  17.         }
  18.         // 通过协程启动服务
  19.         go func() {
  20.                 log.Printf("server listen at %s", srv.Addr)
  21.                 if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
  22.                         log.Fatalf("listen: %s\n", err)
  23.                 }
  24.         }()
  25.         // 制作按Ctrl+C退出功能,此处阻塞
  26.         quit := make(chan os.Signal)
  27.         signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
  28.         <-quit
  29.         // 系统停止开始
  30.         log.Println("Shutdown Server ...")
  31.        
  32.         // 等待2秒
  33.         ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
  34.         defer cancel()
  35.         // 停止服务
  36.         if err := srv.Shutdown(ctx); err != nil {
  37.                 log.Fatal("Server Shutdown:", err)
  38.         }
  39.         // 2秒后打印
  40.         select {
  41.         case <-ctx.Done():
  42.                 log.Println("timeout of 2 seconds.")
  43.         }
  44.         log.Println("Server exiting")
  45. }
复制代码

03-路由




  • 路由通过InitRouter初始化;路由与api操纵分离开来;api的路由设置与路由执行函数分开
  • router/router.go :路由启动,并挂载user模块的路由
  1. package router
  2. import (
  3.         "Gin_gPRC/api/user"
  4.         "github.com/gin-gonic/gin"
  5. )
  6. // Router 接口,规定里面有一个Route函数
  7. type Router interface {
  8.         Route(r *gin.Engine)
  9. }
  10. // 定义一个RegisterRouter 注册类,这个类有一个Route方法
  11. // Route方法接收一个符合Router接口规范的对象
  12. // Route方法里运行了接收对象里的Route方法,用以绑定路由
  13. type RegisterRouter struct{}
  14. func New() *RegisterRouter {
  15.         return &RegisterRouter{}
  16. }
  17. func (*RegisterRouter) Route(router Router, r *gin.Engine) {
  18.         router.Route(r)
  19. }
  20. // 初始化路由
  21. func InitRouter(r *gin.Engine) {
  22.         router := New()
  23.         // 把user.RouterUser对象给到注册类,委托运行了user里的Route方法,传递r参数
  24.         router.Route(&user.RouterUser{}, r)
  25. }
复制代码


  • api/user/route.go;注册了/login/getCaptcha的POST接口,执行函数写再user.go中
  1. package user
  2. import "github.com/gin-gonic/gin"
  3. type RouterUser struct{}
  4. func (*RouterUser) Route(r *gin.Engine) {
  5.         handler := &HandlerUser{}
  6.         r.POST("/login/getCaptcha", handler.getCaptcha)
  7. }
复制代码


  • api/user/user.go;执行POST
  1. package user
  2. import "github.com/gin-gonic/gin"
  3. type HandlerUser struct{}
  4. func (*HandlerUser) getCaptcha(ctx *gin.Context) {
  5.         ctx.JSON(200, "getCaptcha test")
  6. }
复制代码

04-发送验证码




  • 创建消息模子
  1. package model
  2. type BusinessCode int
  3. type Result struct {
  4.         Code BusinessCode `json:"code"`
  5.         Msg  string       `json:"msg"`
  6.         Data any          `json:"data"`
  7. }
  8. func (r *Result) Success(data any) *Result {
  9.         r.Code = 200
  10.         r.Msg = "success"
  11.         r.Data = data
  12.         return r
  13. }
  14. func (r *Result) Fail(code BusinessCode, msg string) *Result {
  15.         r.Code = code
  16.         r.Msg = msg
  17.         return r
  18. }
复制代码


  • 手机验证
  1. package lib
  2. import "regexp"
  3. func CheckMobile(mobile string) bool {
  4.         if mobile == "" {
  5.                 return false
  6.         }
  7.         regular := "^1[3-9]\\d{9}$"
  8.         reg := regexp.MustCompile(regular)
  9.         return reg.MatchString(mobile)
  10. }
复制代码


  • 运行状态代码
  1. package model
  2. const (
  3.         NoLegalMobile BusinessCode = 2001
  4. )
复制代码


  • 修改getCaptcha,返回123456为验证码
  1. package user
  2. import (
  3.         "Gin_gPRC/lib"
  4.         "Gin_gPRC/model"
  5.         "github.com/gin-gonic/gin"
  6.         "log"
  7.         "time"
  8. )
  9. type HandlerUser struct{}
  10. func (*HandlerUser) getCaptcha(ctx *gin.Context) {
  11.         //ctx.JSON(200, "getCaptcha test")
  12.         rsp := &model.Result{}
  13.         //1. 获取参数
  14.         mobile := ctx.PostForm("mobile")
  15.         //2. 校验参数
  16.         if !lib.CheckMobile(mobile) {
  17.                 ctx.JSON(200, rsp.Fail(model.NoLegalMobile, "手机号码错误"))
  18.                 return
  19.         }
  20.         //3. 生成验证码
  21.         code := "123456"
  22.         //4. 调用短信平台接口
  23.         go func() {
  24.                 time.Sleep(2 * time.Second)
  25.                 log.Println("短信平台调用成功")
  26.         }()
  27.         ctx.JSON(200, rsp.Success(code))
  28. }
复制代码

05-redis操纵




  • redis.go,安装:go get github.com/go-redis/redis/v8
  1. package dao
  2. import (
  3.         "context"
  4.         "github.com/go-redis/redis/v8"
  5.         "time"
  6. )
  7. var Rc *RedisCache
  8. type RedisCache struct {
  9.         rdb *redis.Client
  10. }
  11. func init() {
  12.         rdb := redis.NewClient(&redis.Options{
  13.                 Addr:     "localhost:6379",
  14.                 Password: "",
  15.                 DB:       0,
  16.         })
  17.         Rc = &RedisCache{rdb: rdb}
  18. }
  19. func (rc *RedisCache) Put(ctx context.Context, key string, value string, expire time.Duration) error {
  20.         err := rc.rdb.Set(ctx, key, value, expire).Err()
  21.         return err
  22. }
  23. func (rc *RedisCache) Get(ctx context.Context, key string) (string, error) {
  24.         result, err := rc.rdb.Get(ctx, key).Result()
  25.         return result, err
  26. }
复制代码


  • repo/cache.go,定义Cache的接口,再由dao里的redis.go去实现
  1. package repo
  2. import (
  3.         "context"
  4.         "time"
  5. )
  6. type Cache interface {
  7.         Put(ctx context.Context, key string, value string, expire time.Duration) error
  8.         Get(ctx context.Context, key string) (string, error)
  9. }
复制代码


  • user.go,加入redis生存
  1. package user
  2. import (
  3.         "Gin_gPRC/dao"
  4.         "Gin_gPRC/lib"
  5.         "Gin_gPRC/model"
  6.         "Gin_gPRC/repo"
  7.         "context"
  8.         "github.com/gin-gonic/gin"
  9.         "log"
  10.         "time"
  11. )
  12. type HandlerUser struct {
  13.         cache repo.Cache
  14. }
  15. func New() *HandlerUser {
  16.         return &HandlerUser{
  17.                 cache: dao.Rc,
  18.         }
  19. }
  20. func (h *HandlerUser) getCaptcha(ctx *gin.Context) {
  21.         //ctx.JSON(200, "getCaptcha test")
  22.         rsp := &model.Result{}
  23.         //1. 获取参数
  24.         mobile := ctx.PostForm("mobile")
  25.         //2. 校验参数
  26.         if !lib.CheckMobile(mobile) {
  27.                 ctx.JSON(200, rsp.Fail(model.NoLegalMobile, "手机号码错误"))
  28.                 return
  29.         }
  30.         //3. 生成验证码
  31.         code := "123456"
  32.         //4. 调用短信平台接口
  33.         go func() {
  34.                 time.Sleep(2 * time.Second)
  35.                 log.Println("短信平台调用成功")
  36.                 // 制作一个超时的上下文
  37.                 c, cancel := context.WithTimeout(context.Background(), 2*time.Second)
  38.                 defer cancel()
  39.                 // redis加入
  40.                 err := h.cache.Put(c, "REGISTER"+mobile, code, 15*time.Minute)
  41.                 if err != nil {
  42.                         log.Printf("验证码存入redis出错,%v \n", err)
  43.                 }
  44.         }()
  45.         ctx.JSON(200, rsp.Success(code))
  46. }
复制代码

06-日记

安装:go get -u go.uber.org/zap
安装:go get -u github.com/natefinch/lumberjack



  • logs.go
  1. package lib
  2. import (
  3.         "github.com/gin-gonic/gin"
  4.         "github.com/natefinch/lumberjack"
  5.         "go.uber.org/zap"
  6.         "go.uber.org/zap/zapcore"
  7.         "net"
  8.         "net/http"
  9.         "net/http/httputil"
  10.         "os"
  11.         "runtime/debug"
  12.         "strings"
  13.         "time"
  14. )
  15. var lg *zap.Logger
  16. type LogConfig struct {
  17.         DebugFileName string `json:"debugFileName"`
  18.         InfoFileName  string `json:"infoFileName"`
  19.         WarnFileName  string `json:"warnFileName"`
  20.         MaxSize       int    `json:"maxSize"`
  21.         MaxAge        int    `json:"maxAge"`
  22.         MaxBackups    int    `json:"maxBackups"`
  23. }
  24. func InitLogger(cfg *LogConfig) (err error) {
  25.         writeSyncerDebug := getLogWriter(cfg.DebugFileName, cfg.MaxSize, cfg.MaxBackups, cfg.MaxAge)
  26.         writeSyncerInfo := getLogWriter(cfg.InfoFileName, cfg.MaxSize, cfg.MaxBackups, cfg.MaxAge)
  27.         writeSyncerWarn := getLogWriter(cfg.WarnFileName, cfg.MaxSize, cfg.MaxBackups, cfg.MaxAge)
  28.         encoder := getEncoder()
  29.         debugCore := zapcore.NewCore(encoder, writeSyncerDebug, zapcore.DebugLevel)
  30.         infoCore := zapcore.NewCore(encoder, writeSyncerInfo, zapcore.InfoLevel)
  31.         warnCore := zapcore.NewCore(encoder, writeSyncerWarn, zapcore.WarnLevel)
  32.         consoleEncoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig())
  33.         std := zapcore.NewCore(consoleEncoder, zapcore.Lock(os.Stdout), zapcore.DebugLevel)
  34.         core := zapcore.NewTee(debugCore, infoCore, warnCore, std)
  35.         lg = zap.New(core, zap.AddCaller())
  36.         zap.ReplaceGlobals(lg)
  37.         return
  38. }
  39. func getEncoder() zapcore.Encoder {
  40.         encoderConfig := zap.NewProductionEncoderConfig()
  41.         encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
  42.         encoderConfig.TimeKey = "time"
  43.         encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
  44.         encoderConfig.EncodeDuration = zapcore.SecondsDurationEncoder
  45.         encoderConfig.EncodeCaller = zapcore.ShortCallerEncoder
  46.         return zapcore.NewJSONEncoder(encoderConfig)
  47. }
  48. func getLogWriter(filename string, maxSize, maxBackup, maxAge int) zapcore.WriteSyncer {
  49.         lumberJackLogger := &lumberjack.Logger{
  50.                 Filename:   filename,
  51.                 MaxSize:    maxSize,
  52.                 MaxBackups: maxBackup,
  53.                 MaxAge:     maxAge,
  54.         }
  55.         return zapcore.AddSync(lumberJackLogger)
  56. }
  57. func GinLogger() gin.HandlerFunc {
  58.         return func(c *gin.Context) {
  59.                 start := time.Now()
  60.                 path := c.Request.URL.Path
  61.                 query := c.Request.URL.RawQuery
  62.                 c.Next()
  63.                 cost := time.Since(start)
  64.                 lg.Info(path,
  65.                         zap.Int("status", c.Writer.Status()),
  66.                         zap.String("method", c.Request.Method),
  67.                         zap.String("path", path),
  68.                         zap.String("query", query),
  69.                         zap.String("ip", c.ClientIP()),
  70.                         zap.String("user-agent", c.Request.UserAgent()),
  71.                         zap.String("errors", c.Errors.ByType(gin.ErrorTypePrivate).String()),
  72.                         zap.Duration("cost", cost),
  73.                 )
  74.         }
  75. }
  76. func GinRecovery(stack bool) gin.HandlerFunc {
  77.         return func(c *gin.Context) {
  78.                 defer func() {
  79.                         if err := recover(); err != nil {
  80.                                 var brokenPipe bool
  81.                                 if ne, ok := err.(*net.OpError); ok {
  82.                                         if se, ok := ne.Err.(*os.SyscallError); ok {
  83.                                                 if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") {
  84.                                                         brokenPipe = true
  85.                                                 }
  86.                                         }
  87.                                 }
  88.                                 httpRequest, _ := httputil.DumpRequest(c.Request, false)
  89.                                 if brokenPipe {
  90.                                         lg.Error(c.Request.URL.Path,
  91.                                                 zap.Any("error", err),
  92.                                                 zap.String("request", string(httpRequest)))
  93.                                         c.Error(err.(error))
  94.                                         c.Abort()
  95.                                         return
  96.                                 }
  97.                                 if stack {
  98.                                         lg.Error("[Recovery from panic]",
  99.                                                 zap.Any("error", err),
  100.                                                 zap.String("request", string(httpRequest)),
  101.                                                 zap.String("stack", string(debug.Stack())))
  102.                                 } else {
  103.                                         lg.Error("[Recovery from panic]",
  104.                                                 zap.Any("error", err),
  105.                                                 zap.String("request", string(httpRequest)))
  106.                                 }
  107.                                 c.AbortWithStatus(http.StatusInternalServerError)
  108.                         }
  109.                 }()
  110.                 c.Next()
  111.         }
  112. }
复制代码


  • main.go里加入log
  1. package main
  2. import (
  3.         "Gin_gPRC/lib"
  4.         "Gin_gPRC/router"
  5.         "context"
  6.         "github.com/gin-gonic/gin"
  7.         "log"
  8.         "net/http"
  9.         "os"
  10.         "os/signal"
  11.         "syscall"
  12.         "time"
  13. )
  14. func main() {
  15.         r := gin.Default()
  16.         //log
  17.         lc := &lib.LogConfig{
  18.                 DebugFileName: "./logs/debug.log",
  19.                 InfoFileName:  "./logs/info.log",
  20.                 WarnFileName:  "./logs/warn.log",
  21.                 MaxSize:       500,
  22.                 MaxAge:        28,
  23.                 MaxBackups:    3,
  24.         }
  25.         err := lib.InitLogger(lc)
  26.         if err != nil {
  27.                 log.Fatal(err)
  28.         }
  29.         router.InitRouter(r)
  30.         srv := &http.Server{
  31.                 Addr:    ":5000",
  32.                 Handler: r,
  33.         }
  34.         go func() {
  35.                 log.Printf("server listen at %s", srv.Addr)
  36.                 if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
  37.                         log.Fatalf("listen: %s\n", err)
  38.                 }
  39.         }()
  40.         quit := make(chan os.Signal)
  41.         signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
  42.         <-quit
  43.         log.Println("Shutdown Server ...")
  44.         ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
  45.         defer cancel()
  46.         if err := srv.Shutdown(ctx); err != nil {
  47.                 log.Fatal("Server Shutdown:", err)
  48.         }
  49.         select {
  50.         case <-ctx.Done():
  51.                 log.Println("timeout of 2 seconds.")
  52.         }
  53.         log.Println("Server exiting")
  54. }
复制代码


  • 在user.go里应用
  1. package user
  2. import (
  3.         "Gin_gPRC/dao"
  4.         "Gin_gPRC/lib"
  5.         "Gin_gPRC/model"
  6.         "Gin_gPRC/repo"
  7.         "context"
  8.         "github.com/gin-gonic/gin"
  9.         "go.uber.org/zap"
  10.         "time"
  11. )
  12. type HandlerUser struct {
  13.         cache repo.Cache
  14. }
  15. func New() *HandlerUser {
  16.         return &HandlerUser{
  17.                 cache: dao.Rc,
  18.         }
  19. }
  20. func (h *HandlerUser) getCaptcha(ctx *gin.Context) {
  21.         //ctx.JSON(200, "getCaptcha test")
  22.         rsp := &model.Result{}
  23.         //1. 获取参数
  24.         mobile := ctx.PostForm("mobile")
  25.         //2. 校验参数
  26.         if !lib.CheckMobile(mobile) {
  27.                 ctx.JSON(200, rsp.Fail(model.NoLegalMobile, "手机号码错误"))
  28.                 return
  29.         }
  30.         //3. 生成验证码
  31.         code := "123456"
  32.         //4. 调用短信平台接口
  33.         go func() {
  34.                 time.Sleep(2 * time.Second)
  35.                 zap.L().Info("短信平台调用成功")
  36.                 // 制作一个超时的上下文
  37.                 c, cancel := context.WithTimeout(context.Background(), 2*time.Second)
  38.                 defer cancel()
  39.                 // redis加入
  40.                 err := h.cache.Put(c, "REGISTER"+mobile, code, 15*time.Minute)
  41.                 if err != nil {
  42.                         zap.L().Error("验证码存入redis出错" + err.Error())
  43.                 }
  44.         }()
  45.         ctx.JSON(200, rsp.Success(code))
  46. }
复制代码

07-配置

安装:go get github.com/spf13/viper



  • config.yaml
  1. server:
  2.   name: "Gin_gRPC"
  3.   addr: "127.0.0.1:5000"
  4. zap:
  5.   debugFileName: "./logs/debug.log"
  6.   infoFileName: "./logs/info.log"
  7.   warnFileName: "./logs/warn.log"
  8.   maxSize: 500,
  9.   maxAge: 28,
  10.   maxBackups: 3
  11. redis:
  12.   host: "localhost"
  13.   port: 6379
  14.   password: ""
  15.   db: 0
复制代码


  • config/config.go
  1. package config
  2. import (
  3.         "Gin_gPRC/lib"
  4.         "github.com/go-redis/redis/v8"
  5.         "github.com/spf13/viper"
  6.         "log"
  7. )
  8. var Conf = InitConfig()
  9. type Config struct {
  10.         viper *viper.Viper
  11.         SC    *ServerConfig
  12. }
  13. type ServerConfig struct {
  14.         Name string
  15.         Addr string
  16. }
  17. func InitConfig() *Config {
  18.         conf := &Config{viper: viper.New()}
  19.         //workDir, _ := os.Getwd()
  20.         // 确定配置文件的名称、类型与位置
  21.         conf.viper.SetConfigName("config")
  22.         conf.viper.SetConfigType("yaml")
  23.         conf.viper.AddConfigPath("./")
  24.         err := conf.viper.ReadInConfig()
  25.         if err != nil {
  26.                 log.Fatalf("Fatal error config file: %s \n", err)
  27.         }
  28.         // 读取Server的配置
  29.         conf.ReadServerConfig()
  30.         // 初始化zapLog
  31.         conf.InitZapLog()
  32.         return conf
  33. }
  34. func (c *Config) InitZapLog() {
  35.         lc := &lib.LogConfig{
  36.                 DebugFileName: c.viper.GetString("zap.debugFileName"),
  37.                 InfoFileName:  c.viper.GetString("zap.infoFileName"),
  38.                 WarnFileName:  c.viper.GetString("zap.warnFileName"),
  39.                 MaxSize:       c.viper.GetInt("zap.maxSize"),
  40.                 MaxAge:        c.viper.GetInt("zap.maxAge"),
  41.                 MaxBackups:    c.viper.GetInt("zap.maxBackups"),
  42.         }
  43.         err := lib.InitLogger(lc)
  44.         if err != nil {
  45.                 log.Fatal(err)
  46.         }
  47. }
  48. func (c *Config) ReadServerConfig() {
  49.         sc := &ServerConfig{}
  50.         sc.Name = c.viper.GetString("server.name")
  51.         sc.Addr = c.viper.GetString("server.addr")
  52.         c.SC = sc
  53. }
  54. func (c *Config) ReadRedisConfig() *redis.Options {
  55.         return &redis.Options{
  56.                 Addr:     c.viper.GetString("redis.host") + ":" + c.viper.GetString("redis.port"),
  57.                 Password: c.viper.GetString("redis.password"),
  58.                 DB:       c.viper.GetInt("redis.db"),
  59.         }
  60. }
复制代码


  • 在main.go中应用
  1. func main() {
  2.         r := gin.Default()
  3.        
  4.         config.InitConfig()
  5.         router.InitRouter(r)
  6.         srv := &http.Server{
  7.                 Addr:    config.Conf.SC.Addr,
  8.                 Handler: r,
  9.         }
  10.         ...
  11. }
复制代码


  • 在redis.go中应用
  1. func init() {
  2.         rdb := redis.NewClient(config.Conf.ReadRedisConfig())
  3.         Rc = &RedisCache{rdb: rdb}
  4. }
复制代码
结束:

   对于微服务,思量实验下利用Go-micro + Gin的方式,后续继续记录

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

八卦阵

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表