• 初始化一个Gin框架的Go-Web项目


    使用到的第三方库

    • gin Gin 框架
    • viper 配置文件管理
    • cors 跨域资源请求配置
    • gorm ORM 库
    • zap 日志记录

    main 包

    Go 语言程序的入口点

    main.go 文件

    1. 使用 flag 读取配置文件路径参数,默认当前目录下
    2. 使用 viper 读取 config.ini 配置文件初始化初始数据
    3. 初始化随机数种子
    4. 初始化数据库
    5. 声明启动程序模式
    6. 配置启动参数并启动服务
    package main
    
    import (
    	"flag"
    	"log"
    	"math/rand"
    	"project/dao"
    	"project/routers"
    	"time"
    
    	"github.com/gin-gonic/gin"
    	"github.com/spf13/viper"
    )
    
    var (
    	ServiceHost  string
    	ServicePort  string
    )
    
    func init() {
    	var configPath string
    	flag.StringVar(&configPath, "config-path", ".", "path to config file")
    	flag.Parse()
    	viper.SetConfigName("config")   // 设置配置文件名为“config”
    	viper.SetConfigType("ini")      // 设置配置文件类型为“ini”
    	viper.AddConfigPath(configPath) // 设置在configPath中查找配置文件
    	if err := viper.ReadInConfig(); err != nil {
    		if _, ok := err.(viper.ConfigFileNotFoundError); ok {
    			// Config file not found; ignore error if desired
    			log.Panic("没有找到配置文件")
    		} else {
    			// Config file was found but another error was produced
    			log.Panic("初始化配置出错", err.Error())
    		}
    	}
    	ServiceHost = viper.GetString("service.host")
    	ServicePort = viper.GetString("service.port")
    	dao.DatabaseUser = viper.GetString("database.user")
    	dao.DatabasePwd = viper.GetString("database.pwd")
    	dao.DatabaseHost = viper.GetString("database.host")
    	dao.DatabasePort = viper.GetString("database.port")
    	dao.DatabaseName = viper.GetString("database.name")
    }
    func main() {
    	rand.Seed(time.Now().Unix())
    	mode := "release"
    	err := dao.InitMySQL()
    	if err != nil {
    		log.Println("初始化数据失败", err.Error())
    		return
    	}
    	switch mode {
    	case "debug":
    		gin.SetMode(gin.DebugMode)
    	case "release":
    		gin.SetMode(gin.ReleaseMode)
    	case "test":
    		gin.SetMode(gin.TestMode)
    	}
    	r := routers.SetupRouter()
    	s := &http.Server{
    		Addr:         ServiceHost + ":" + ServicePort,
    		Handler:      r,
    		ReadTimeout:  5 * time.Second,
    		WriteTimeout: 10 * time.Second,
    	}
    	err = s.ListenAndServe()
    	if err != nil {
    		panic(err)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71

    dao 包

    通常被用于数据存储层的实现,可将底层数据库访问操作封装隐藏细节以简化数据库操作

    mysql.go 文件

    使用 gorm 初始化 MySQL 数据库连接

    package dao
    
    import (
    	"fmt"
    	"log"
    
    	"gorm.io/driver/mysql"
    	"gorm.io/gorm"
    )
    
    var (
    	Db           *gorm.DB
    	DatabaseUser string
    	DatabasePwd  string
    	DatabaseHost string
    	DatabasePort string
    	DatabaseName string
    )
    
    func InitMySQL() (err error) {
    	dsn := fmt.Sprintf("%v:%v@tcp(%v:%v)/%v?charset=utf8mb4&parseTime=True&loc=Local",
    		DatabaseUser, DatabasePwd, DatabaseHost, DatabasePort, DatabaseName)
    	Db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
    	if err != nil {
    		log.Panic("连接数据库失败", err.Error())
    	}
    	// 测试数据库连接
    	err = Db.Exec("SELECT 1").Error
    	if err != nil {
    		log.Panic("数据库连接失败", err.Error())
    	}
    	return
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    routers 包

    通常用于路由器配置,这个包中包含的代码需要读取 HTTP 请求并将该请求发送到相应后端的处理程序,然后从处理程序获取响应

    routers.go 文件

    1. 创建日志记录器
    2. 初始化 gin 引擎
    3. 配置 cors 跨域请求
    4. 设置一个默认路由,及无请求到路由时返回的数据
    package routers
    
    import (
    	"net/http"
    	"project/controller"
    
    	"github.com/gin-contrib/cors"
    	"github.com/gin-gonic/gin"
    	"go.uber.org/zap"
    )
    
    func SetupRouter() *gin.Engine {
    	// 创建一个生产级别的日志记录器
    	logger, err := zap.NewProduction()
    	if err != nil {
    		panic(err)
    	}
    	// 在函数结束后刷新缓冲区
    	defer logger.Sync()
    	r := gin.Default()
    	// 将 logger 存储在 Gin 的中间件中
    	r.Use(func(c *gin.Context) {
    		c.Set("logger", logger)
    		c.Next()
    	})
    
    	r.Use(cors.New(cors.Config{
    		AllowOrigins:     []string{"*"},
    		AllowMethods:     []string{"POST, GET, PUT, DELETE, OPTIONS"},
    		AllowHeaders:     []string{"Content-Type, Content-Length"},
    		ExposeHeaders:    []string{"Access-Control-Allow-Headers"},
    		AllowCredentials: true,
    	}))
    	r.GET("/first", controller.FirstHandler)
    
    	// 下面有两个配置
    	// 如果将前端服务与后端服务同时启动可以设置第一种,将前端打包的文件放到 public 文件夹内
    	// 我这里设置的使用 vite 打包,所以就下面这种设置,可以自行更改
    	// 当直接访问的时候,就相当于请求"/"路由,就会访问 index.html 页面
    	// 设置静态文件服务目录
    	r.Static("public", "public")
    	r.Static("assets", "public/assets")
    	r.GET("/", func(c *gin.Context) {
    		c.File("public/index.html")
    	})
    	r.NoRoute(func(c *gin.Context) {
    		c.File("public/index.html")
    	})
    
    	// 如果前后端分离模式,可以设置为请求到没有的路由就返回 Not Found
    	r.NoRoute(func(c *gin.Context) {
    		c.JSON(http.StatusNotFound, gin.H{
    			"msg": "Not Found",
    		})
    	})
    
    	return r
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    controller 包

    负责维护业务逻辑的实现,同时调用 Dao 对象实现数据存储、检索等功能,通常与用户交互并处理相关活动

    controller.go 文件

    1. 创建初始默认路由函数,使用 info 级别的日志记录并返回数据
    package controller
    
    import (
    	"net/http"
    
    	"github.com/gin-gonic/gin"
    	"go.uber.org/zap"
    )
    
    func FirstHandler(c *gin.Context) {
    	c.MustGet("logger").(*zap.Logger).Info("这是一个Info日志")
    	c.JSON(http.StatusOK, gin.H{
    		"code": 1,
    		"data": "Hello World",
    		"msg":  "成功",
    	})
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    config.ini 文件

    外部配置文件

    [service]
    host=127.0.0.1
    port=8000
    [database]
    user=root
    pwd=123456
    host=127.0.0.1
    port=3306
    name=databasename
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    build.bat 文件

    打包脚本,不用每次修改环境变量,不用手动输入打包命令
    脚本内容:修改环境变量为 arm64 架构 Linux 系统,打包,还原为 amd64 架构 Windows 系统,我这里使用 Windows 做开发,需要部署到 arm64 架构的 Linux 服务器上,所以这样写,根据需要自行更改

    @echo off
    
    go env -w CGO_ENABLED=0
    go env -w GOOS=linux
    go env -w GOARCH=arm64
    go build -ldflags "-w -s"
    go env -w CGO_ENABLED=1
    go env -w GOOS=windows
    go env -w GOARCH=amd64
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
  • 相关阅读:
    [机缘参悟-64]:《兵者,诡道也》-5-孙子兵法解读-混战计
    现代_复习_第1章:行列式
    队列的两种实现方式---数组+链表
    rxjs Observable 自定义 Operator 的开发技巧
    海藻酸钠-四嗪|TZ-PEG-alginate|海藻酸钠-peg-四嗪TZ
    [rust] Rust与C++20编码习惯对照
    区块链论文速读A会-ISSTA 2023(1/2)法律协议如何变成智能合约代码?
    【Java】才疏学浅·小石Java问道之路
    JavaScript sort() 方法你真的了解吗?
    【HDFS】客户端写数据时,dataQueue的几处wait方法的调用场景
  • 原文地址:https://blog.csdn.net/sywdebug/article/details/132763230