引言
- 下载安装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、下一节我们定义项目目录结构
本章节代码示例
系列文章