gin框架实践连载二 | 安装Gin

1,989 阅读3分钟

引言

  • 下载安装gin,并按照demo运行
  • 自定义项目结构
  • 封装config层,定义配置文件
  • github代码地址

1、下载gin

go get github.com/gin-gonic/gin

2、新建main.go

package main

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

func main() {
    r := gin.Default()
    r.GET("/ping", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "pong",
        })
    })
    r.Run() // listen and serve on 0.0.0.0:8080
}
# 运行 go run main.go 然后在浏览器访问 http://127.0.0.1:8080/ping
go  run  main.go

到这gin已经跑起来了,可以进行下一步开发了

3、项目结构

实际项目业务功能和模块会很多,我们不可能把所有代码都写在一个go文件里面或者写在一个main入口函数里面;我们需要对项目结构做一些规划,方便维护代码以及扩展。

Gin框没有对项目结构做出限制,我们可以根据自己项目需要自行设计。

  • app (项目核心目录)
    • controller(控制器层)
    • models(数据层)
    • middleware(中间件层)
    • services(服务层)
    • requests (请求校验层)
  • config (配置文件目录)
  • routes (路由)
  • tests (测试类)
  • tool (全局函数,工具类)
  • static(静态文件css、js、img)
  • view (如果需要模板)

4、抽离config层

常见的做配置管理的第3方包,我们采用 ini

在config下新建 app.ini

#debug or release
RUN_MODE = debug

[app]
Template = view/**/*
PageSize = 10
JwtSecret = 23347$040412
SigningMethod = HS256 
JwtExpiresAt = 5

[server]
HttpAddress=0.0.0.0
HttpPort = 8080
ReadTimeout = 60
ReadTimeout = 60

[database-mysql]
MysqlUser = mysqluser
MysqlPassword = mysqlpws
MysqlHost = mysqlhost
MysqlName = test
MysqlPrefix = test_

在config下新建 config.go

重点提醒:为什么我要用runtime来获取config目录,因为我们的目录设置test单独了一个目录,单元测试的时候config会有问题

package config


import (
	"gopkg.in/ini.v1"
	"log"
	"fmt"
	"os"
	"time"
	"errors"
	"runtime"
)

//app struct
type App struct {
	Template string
	PageSize int
	JwtSecret string
	JwtExpiresAt time.Duration
	SigningMethod string
}

var AppSetting = &App{}

//server struct
type Server struct {
	HttpAddress string
	HttpPort int
	ReadTimeout  time.Duration
	WriteTimeout  time.Duration
}

var ServerSetting = &Server{}

//Mysql struct
type Mysql struct {
	MysqlUser string
	MysqlPassword string
	MysqlHost string
	MysqlName string
	MysqlPrefix string
	MaxLifetime time.Duration
}

var MysqlSetting = &Mysql{}

var (
	Cfg *ini.File
	RunMode string
	configPathError = errors.New("Can not get current file info")
	currentPath string = currentFile() //重点获取config的目录
)

//get config path Single
func getConfigPath(path string) (file string){
	return fmt.Sprintf("%s/%s",path,"app.ini")
}

func init(){
	InitConfig()
}

func currentFile() string {
	_, file, _, ok := runtime.Caller(1)
	if !ok {
		panic(configPathError)
	}
	return fmt.Sprintf("%s/..",file)
}

func InitConfig() {
	iniPath := getConfigPath(currentPath)
	var err error
	Cfg, err = ini.Load(iniPath)
    if err != nil {
        fmt.Printf("Fail to read file: %v", err)
        os.Exit(1)
	}
	RunMode = Cfg.Section("").Key("RUN_MODE").MustString("debug")
	LoadBasice()
	LoadApp()
	LoadServer()
	LoadDatabase()
}

//加载基础配置
func LoadBasice() {
	RunMode = Cfg.Section("").Key("RUN_MODE").MustString("debug")
}

//加载app配置
func LoadApp() {
	sec, err := Cfg.GetSection("app")
    if err != nil {
        log.Fatalf("Fail to get section 'app': %v", err)
	}
	
	err = sec.MapTo(AppSetting)
	if err != nil {
        log.Fatalf("Cfg.MapTo AppSetting err: %v", err)
    }

	AppSetting.JwtExpiresAt = time.Duration(sec.Key("JWT_EXPIRE_TIME").MustInt(10))*time.Minute
}

//加载http服务配置
func LoadServer() {
	sec, err := Cfg.GetSection("server")
    if err != nil {
        log.Fatalf("Fail to get section 'server': %v", err)
	}
	
	err = sec.MapTo(ServerSetting)
	if err != nil {
        log.Fatalf("Cfg.MapTo ServerSetting err: %v", err)
    }

    ServerSetting.ReadTimeout = time.Duration(sec.Key("ReadTimeout").MustInt(60)) * time.Second
    ServerSetting.WriteTimeout =  time.Duration(sec.Key("WriteTimeout").MustInt(60)) * time.Second 
}

//加载数据库配置
func LoadDatabase() {
	sec, err := Cfg.GetSection("database-mysql")
    if err != nil {
        log.Fatalf("Fail to get section 'app': %v", err)
    }

	err = sec.MapTo(MysqlSetting)
	if err != nil {
        log.Fatalf("Cfg.MapTo MysqlSetting err: %v", err)
	}

	MysqlSetting.MaxLifetime = time.Duration(sec.Key("MaxLifetime").MustInt(60)) * time.Second
}




tests 目录新增main_test.go

package tests

import (
    "testing"
	"fmt"
	"os"

	"github.com/gin-gonic/gin"
	"go-api/config"
)


func setup() {
	gin.SetMode(gin.TestMode)
	//设置config模式
	config.SetConfigMode("test") 
	config.InitConfig()
	//打印config
	fmt.Println(config.AppSetting.JwtSecret);
	fmt.Println("Before all tests")
}

func teardown() {
	fmt.Println("After all tests")
}
func TestMain(m *testing.M)  {
	setup()
    fmt.Println("Test begins....")
	code := m.Run() // 如果不加这句,只会执行Main
	teardown()
	os.Exit(code)
}

执行单元测试

go  test testes/*
  • 修改mian.go
package main

import (
    "fmt"
    
    "go-api/config"

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

func main() {
    gin.SetMode(config.RunMode)
    r := gin.Default()
    r.GET("/ping", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "pong",
        })
    })

    r.Run(fmt.Sprintf("%s:%d",config.ServerSetting.HttpAddress,config.ServerSetting.HttpPort)) // listen and serve on 0.0.0.0:8080 
}

执行

go run main.go

5、下一节我们定义项目目录结构

本章节代码示例

系列文章