在CSDN学Golang场景化解决方案(基于gin框架的web开发脚手架)

一,中间件统一实现Oauth2身份验证

在Golang基于Gin框架开发Web应用程序时,可以使用gin-oauth2来实现Oauth2身份验证。下面是简单的步骤:

  1. 安装gin-oauth2包:go get github.com/appleboy/gin-oauth2
  2. 导入依赖:import "github.com/appleboy/gin-oauth2"
  3. 创建oauth2中间件:
func oauthMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        config := oauth2.Config{
            ClientID:     "CLIENT_ID",
            ClientSecret: "CLIENT_SECRET",
            Endpoint:     google.Endpoint,
        }
        token, err := oauth2.New(config).ValidateToken(c.Request)
        if err != nil {
            c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid Token"})
            c.Abort()
            return
        }
        // 将token存储到context中以供后续处理使用
        c.Set("token", token)
    }
}
  1. 在路由中使用oauth2中间件:
router.GET("/private", oauthMiddleware(), func(c *gin.Context) {
    // 从context中获取token并进行相关处理
    token, exists := c.Get("token")
    if !exists {
        // handle error
    }
    ...
})

这样,在访问路由"/private"之前,会先执行oauthMiddleware()函数进行身份验证,只有通过验证才能访问该路由。

需要注意的是,以上示例使用了Google作为OAuth提供商,如果你要使用其他提供商,请根据其文档更新配置信息即可。

二,路由分组api版本控制

在Golang基于Gin框架开发Web应用程序时,可以使用路由分组和api版本控制来管理不同版本的API。下面是简单的步骤:

  1. 导入依赖:import "github.com/gin-gonic/gin"
  2. 创建路由组:
v1 := router.Group("/v1")
{
    v1.GET("/users", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{"message": "API V1"})
    })
}
v2 := router.Group("/v2")
{
    v2.GET("/users", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{"message": "API V2"})
    })
}
  1. 在需要进行版本控制的地方,根据请求头部信息决定调用哪个版本的API:
router.GET("/users", func(c *gin.Context) {
    version := c.GetHeader("Accept-Version")
    switch version {
        case "application/vnd.example.v1+json":
            // 调用V1版本的API
            break
        case "application/vnd.example.v2+json":
            // 调用V2版本的API
            break
        default:
            // 版本不支持或未指定
            c.JSON(http.StatusUnsupportedMediaType, gin.H{"error": "Unsupported Media Type"})
            return
    }
})

这样,在访问"/v1/users"或"/v2/users"时,会分别调用对应版本的API;而在访问"/users"时,则会根据请求头部信息来决定调用哪个版本的API。

需要注意的是,以上示例中使用了自定义的请求头部信息来指定API版本,如果你想使用其他方式进行版本控制,也可以根据实际情况进行修改。

三,jwt实现客户端令牌

在Golang基于Gin框架开发Web应用程序时,可以使用JWT(JSON Web Token)来实现客户端令牌。下面是简单的步骤:

  1. 导入依赖:import "github.com/dgrijalva/jwt-go"
  2. 定义JWT生成函数:
func generateToken(userId int64) (string, error) {
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
        "userId": userId,
        "exp": time.Now().Add(time.Hour * 24).Unix(),
    })
    return token.SignedString([]byte("your-secret-key"))
}
  1. 定义JWT验证中间件:
func authMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        tokenString := c.GetHeader("Authorization")
        if tokenString == "" {
            c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
            return
        }
        claims := jwt.MapClaims{}
        token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
            return []byte("your-secret-key"), nil
        })
        if err != nil || !token.Valid {
            c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Invalid Token"})
            return
        }
        userIdFloat64, ok := claims["userId"].(float64)
        if !ok {
            c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Invalid User ID"})
            return
        }
        userId := int64(userIdFloat64)
        
        // 将用户ID存储到上下文中,以便后续使用
        c.Set("userId", userId)
        
        // 继续处理请求
        c.Next()
    }
}
  1. 在需要进行认证的路由上应用中间件:
router.GET("/users/:id", authMiddleware(), func(c *gin.Context) {
    userId := c.GetInt64("userId")
    id, err := strconv.ParseInt(c.Param("id"), 10, 64)
    if err != nil || userId != id {
        c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
        return
    }
    // 处理请求...
})

这样,在访问"/users/:id"时,会检查请求头部信息中的Authorization字段是否为有效的JWT令牌,并将其中包含的用户ID存储到上下文中。在后续处理中,可以通过从上下文中获取用户ID来实现权限控制。

需要注意的是,以上示例中使用了简单的对称加密方式来生成和验证JWT令牌,如果你想使用其他加密算法或更复杂的认证方案,也可以根据实际情况进行修改。

四,logurs日志组件封装

在Golang基于Gin框架开发Web应用程序时,可以使用logrus来实现日志记录。下面是一个简单的封装示例:

  1. 导入依赖:import log "github.com/sirupsen/logrus"
  2. 定义初始化函数:
func initLogger() {
    // 设置日志格式为JSON格式
    log.SetFormatter(&log.JSONFormatter{})

    // 设置日志级别为debug以上
    log.SetLevel(log.DebugLevel)

    // 输出到文件
    file, err := os.OpenFile("app.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
    if err == nil {
        log.SetOutput(file)
    } else {
        log.Info("Failed to log to file, using default stderr")
    }

    // 输出到控制台
    log.SetOutput(os.Stdout)
}
  1. 在main函数中调用初始化函数:
func main() {
   initLogger()
   
   // ...
   
   router.Run(":8080")
}
  1. 封装logger对象:
type Logger struct {
}

func (l *Logger) Info(args ...interface{}) {
	log.Info(args...)
}

func (l *Logger) Warn(args ...interface{}) {
	log.Warn(args...)
}

func (l *Logger) Error(args ...interface{}) {
	log.Error(args...)
}

func (l *Logger) Fatal(args ...interface{}) {
	log.Fatal(args...)
}

func (l *Logger) Panic(args ...interface{}) {
	log.Panic(args...)
}
  1. 在需要记录日志的地方使用封装后的logger对象:
logger := &Logger{}
logger.Info("message")

这样,在记录日志时,就可以通过封装后的logger对象来实现。

需要注意的是,以上示例中使用了logrus来输出日志到文件和控制台。如果你想使用其他方式(例如输出到ELK等),也可以根据实际情况进行修改。

五,分布式日志链路追踪设计

在Golang基于Gin框架开发Web应用程序时,为了方便进行分布式日志链路追踪,可以使用Jaeger等开源工具。下面是一个简单的设计示例:

  1. 导入依赖:import ( "github.com/gin-gonic/gin" opentracing "github.com/opentracing/opentracing-go" jaegercfg "github.com/uber/jaeger-client-go/config" log "github.com/sirupsen/logrus" )
  2. 定义初始化函数:
func initTracer() {
    // 配置jaeger客户端
    cfg, err := jaegercfg.FromEnv()
    if err != nil {
        log.WithError(err).Fatal("Could not parse Jaeger env vars")
    }
    
    tracer, closer, err := cfg.NewTracer(jaegercfg.Logger(log.StandardLogger()))
    if err != nil {
        log.WithError(err).Fatal("Could not initialize jaeger tracer")
    }
    
    // 设置全局tracer
    opentracing.SetGlobalTracer(tracer)
    
    // 注册gin中间件
    router.Use(func(c *gin.Context) {
        spanCtx, _ := tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(c.Request.Header))
        span := tracer.StartSpan(c.Request.URL.Path, ext.RPCServerOption(spanCtx))
        
        defer span.Finish()
        
        c.Set("span", span)
        
        c.Next()
        
        statusCode := c.Writer.Status()
        
        if statusCode >= 400 && statusCode < 500 {
            span.SetTag("error", true)
        } else {
            span.SetTag("success", true)
        }
        
        span.SetTag("http.status_code", statusCode)
    })
    
    // defer关闭tracer
    defer closer.Close()
}
  1. 在main函数中调用初始化函数:
func main() {
   initTracer()
   
   // ...
   
   router.Run(":8080")
}
  1. 在需要进行日志记录的地方,使用上下文中的span对象进行追踪:
func someHandler(c *gin.Context) {
    span, ok := c.Get("span")
    
    if !ok {
        log.Error("Could not retrieve span from context")
        return
    }
    
    // 使用span记录日志和追踪
    
    span.LogFields(log.Fields{
        "event": "some_event",
        "value": 42,
    })
    
    // ...
}

这样,在Web应用程序中就可以通过Jaeger等开源工具,实现方便的分布式日志链路追踪了。

Golang云原生学习路线图、教学视频、文档资料、面试题资料(资料包括C/C++、K8s、golang项目实战、gRPC、Docker、DevOps等)免费分享 有需要的可以加qun:793221798领取

六,EFK统一日志采集

在Golang基于Gin框架开发Web应用程序时,可以使用EFK等工具实现统一日志采集。下面是一个简单的设计示例:

  1. 导入依赖:import ( "github.com/gin-gonic/gin" "github.com/elastic/go-elasticsearch/v7" "go.uber.org/zap" )
  2. 初始化Logger:
func initLogger() {
    config := zap.NewDevelopmentConfig()
    
    logger, err := config.Build()
    
    if err != nil {
        log.WithError(err).Fatal("Could not initialize logger")
    }
    
    // 设置全局Logger
    zap.ReplaceGlobals(logger)
}
  1. 在main函数中调用初始化函数:
func main() {
   initLogger()
   
   // ...
   
   router.Run(":8080")
}
  1. 定义logrus-zap-hook并注册到logrus中:
type LogrusZapHook struct {}

func (hook *LogrusZapHook) Levels() []log.Level {
    return log.AllLevels
}

func (hook *LogrusZapHook) Fire(entry *log.Entry) error {
    switch entry.Level {
        case log.DebugLevel:
            zap.L().Debug(entry.Message)
        case log.InfoLevel:
            zap.L().Info(entry.Message)
        case log.WarnLevel:
            zap.L().Warn(entry.Message)
        case log.ErrorLevel:
            zap.L().Error(entry.Message)
        case log.FatalLevel:
            zap.L().Fatal(entry.Message)
        default:
            panic(fmt.Sprintf("Unhandled log level: %v", entry.Level))
     }
     
     return nil
}

func registerLogrusZapHook() {
    hook := &LogrusZapHook{}
    log.AddHook(hook)
}
  1. 在需要进行日志记录的地方,使用logrus进行记录:
func someHandler(c *gin.Context) {
    // 使用logrus记录日志
    log.WithFields(log.Fields{
        "event": "some_event",
        "value": 42,
    }).Info("Hello, world!")
    
    // ...
}
  1. 配置EFK中的Logstash或Filebeat等采集工具,将应用程序的日志发送到Elasticsearch。

这样,在Web应用程序中就可以通过EFK等工具,实现统一的日志采集和管理了。

七,viper配置文件读取

在Golang基于Gin框架开发Web应用程序时,可以使用Viper库实现配置文件读取。下面是一个简单的设计示例:

  1. 导入依赖:import ( "github.com/gin-gonic/gin" "github.com/spf13/viper" )
  2. 初始化Viper:
func initViper() {
    viper.SetConfigName("config") // 配置文件名字
    viper.AddConfigPath(".") // 配置文件所在路径
    
    err := viper.ReadInConfig()
    
    if err != nil {
        log.WithError(err).Fatal("Could not read config file")
    }
}
  1. 在main函数中调用初始化函数:
func main() {
   initViper()
   
   // ...
   
   router.Run(":8080")
}
  1. 定义配置结构体,并使用viper将配置信息绑定到结构体上:
type Config struct {
    Mode     string `mapstructure:"mode"`
    Port     int    `mapstructure:"port"`
}

func readConfig() (*Config, error) {
	config := &Config{}

	err := viper.Unmarshal(config)
	if err != nil {
		return nil, err
	}

	return config, nil
}
  1. 在需要访问配置信息的地方,通过读取配置结构体来获取配置信息:
func someHandler(c *gin.Context) {
	config := &Config{}
	
	err := viper.Unmarshal(config)
	if err != nil {
		log.WithError(err).Error("Failed to read configuration")
		return
	}
	
	mode := config.Mode
	port := config.Port
	
	// ...
}

这样,在Web应用程序中就可以使用Viper库,方便地读取配置文件中的信息了。

八,etcd应用配置中心

在Golang基于Gin框架开发Web应用程序时,可以使用Etcd作为应用配置中心。下面是一个简单的设计示例:

  1. 导入依赖:import ( "github.com/gin-gonic/gin" "go.etcd.io/etcd/clientv3" )
  2. 初始化Etcd客户端:
func initEtcd() (*clientv3.Client, error) {
	config := clientv3.Config{
		Endpoints: []string{"http://localhost:2379"},
	}

	client, err := clientv3.New(config)
	if err != nil {
		return nil, err
	}

	return client, nil
}
  1. 在main函数中调用初始化函数:
func main() {
   etcdClient, err := initEtcd()
   if err != nil {
      log.WithError(err).Fatal("Could not connect to Etcd")
   }
   
   // ...
   
   router.Run(":8080")
}
  1. 定义获取配置信息的函数,并通过Etcd客户端从配置中心读取配置信息:
type Config struct {
    Mode     string `json:"mode"`
    Port     int    `json:"port"`
}

func getConfig(client *clientv3.Client) (*Config, error) {
	resp, err := client.Get(context.Background(), "/app/config")
	if err != nil {
		return nil, err
	}

	for _, kv := range resp.Kvs {
		var config Config

		err = json.Unmarshal(kv.Value, &config)
		if err != nil {
			return nil, err
		}

        return &config, nil
    }

    return nil, errors.New("no configuration found in Etcd")
}
  1. 在需要访问配置信息的地方,通过调用获取配置函数来获取配置信息:
func someHandler(c *gin.Context) {
    config, err := getConfig(etcdClient)
	if err != nil {
		log.WithError(err).Error("Failed to read configuration")
		return
	}
	
	mode := config.Mode
	port := config.Port
	
	// ...
}

这样,在Web应用程序中就可以使用Etcd作为应用配置中心,实现动态配置管理了。

九,redis数据缓存

在Golang基于Gin框架开发Web应用程序时,可以使用Redis作为数据缓存。下面是一个简单的设计示例:

  1. 导入依赖:import ( "github.com/gin-gonic/gin" "github.com/go-redis/redis/v8" )
  2. 初始化Redis客户端:
func initRedis() *redis.Client {
	return redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "", // no password set
		DB:       0,  // use default DB
	})
}
  1. 在main函数中调用初始化函数:
func main() {
   redisClient := initRedis()
   
   // ...
   
   router.Run(":8080")
}
  1. 定义获取数据的函数,并通过Redis客户端从缓存中读取数据:
func getDataFromCache(redisClient *redis.Client, key string) (string, error) {
	data, err := redisClient.Get(context.Background(), key).Result()
	if err != nil && err != redis.Nil {
		return "", err
	}

	return data, nil
}
  1. 定义写入数据的函数,并通过Redis客户端将数据写入缓存:
func setDataToCache(redisClient *redis.Client, key string, value interface{}, expiration time.Duration) error {
	err := redisClient.Set(context.Background(), key, value, expiration).Err()
	if err != nil {
		return err
	}

	return nil
}
  1. 在需要访问数据的地方,通过调用获取或写入数据的函数来进行操作:
func someHandler(c *gin.Context) {
    data, err := getDataFromCache(redisClient, "data_key")
	if err != nil {
		log.WithError(err).Error("Failed to read data from cache")
		return
	}
	
	if data == "" {
	    // 数据不存在缓存中,需要从其他数据源获取,并将其写入缓存
	    
	    // ...
	    
	    err = setDataToCache(redisClient, "data_key", someData, 10*time.Minute)
		if err != nil {
			log.WithError(err).Error("Failed to write data to cache")
			return
		}
		
		data = someData
    }
    
    // 处理数据
    
    // ...
}

这样,在Web应用程序中就可以使用Redis作为数据缓存了。在处理请求时,首先尝试从Redis缓存中获取数据,如果数据不存在,则从其他数据源获取,并将其写入Redis缓存。下次再有相同的请求时,直接从Redis缓存中读取即可。

十,mysql数据存储

在Golang基于Gin框架开发Web应用程序时,可以使用MySQL作为数据存储。下面是一个简单的设计示例:

  1. 导入依赖:import ( "github.com/gin-gonic/gin" "gorm.io/gorm" "gorm.io/driver/mysql" )
  2. 初始化数据库:
func initDB() (*gorm.DB, error) {
	dsn := "user:password@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"

	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		return nil, err
	}

	return db, nil
}
  1. 在main函数中调用初始化函数,并迁移模型:
func main() {
   db, err := initDB()
   if err != nil {
       log.Fatal(err)
   }
   
   // 迁移模型
   err = db.AutoMigrate(&User{})
   if err != nil {
       log.Fatal(err)
   }
   
   // ...
   
   router.Run(":8080")
}
  1. 定义模型结构体:
type User struct {
	gorm.Model

	Name  string `json:"name"`
	Email string `json:"email"`
}
  1. 定义处理请求的函数,通过ORM操作数据库:
func createUser(c *gin.Context) {
	var user User
	
	err := c.ShouldBindJSON(&user)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
		return
	}
	
	result := db.Create(&user)
	if result.Error != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create user"})
		return
	}
	
	c.JSON(http.StatusCreated, user)
}
  1. 在需要访问数据的地方,通过调用ORM方法来进行操作:
func getUser(c *gin.Context) {
    var user User
    
    id := c.Param("id")
    
    result := db.First(&user, id)
    if result.Error != nil {
        if errors.Is(result.Error, gorm.ErrRecordNotFound) {
            c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
        } else {
            c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get user"})
        }
        return
    }
    
    c.JSON(http.StatusOK, user)
}

这样,在Web应用程序中就可以使用MySQL作为数据存储了。在处理请求时,通过ORM操作数据库进行增删改查等操作。

你可能感兴趣的:(golang,gin,开发语言,后端,云原生)