今天內容的commit
大多細節沒有辨法一一說明XD
完整的檔案跟程式都在上面
Golang寫起來其實很自由,專案結構要很平只有一層也可以,要多層巢狀的也可以,根據你的專案大小來決定,如果功能少少的也不太會擴張,就不要設計成太多層,只會搞到自己。
下面這個模式是我個人用得滿習慣,做後端的API服務應該都適用
config
讀取設定檔
handler
相關API主要邏輯實作的地方
model
全部有關資料庫的操作都放在這邊
pkg
共用的一些func
middleware
中間層,例如登入狀態的驗證
專案比較大的話,我會多拆一個文件夾來放商業邏輯,handler就只做流程的控制
如果這樣還不夠放,會建議拆成一個以上的服務,服務之間用API來溝通。用微服務的架構來避免單一個專案太過巨大,導致以後難以維護。
這邊用到一個叫gin的套件
主要是用來處理一些HTTP的東西,例如router、cookie什麼的,有興趣可以看一下他們的文件
為了系統輸出的格式保持一致,我寫了一些func
request的成功失敗都寫在result這個key
失敗會有相關的error code
pkg/res/res.go
// Success Success
func Success(c *gin.Context, payload interface{}) {
c.JSON(http.StatusOK, gin.H{
"result": true,
"data": payload,
})
}
// SystemError SystemError
func SystemError(c *gin.Context, errorCode int, payload interface{}) {
c.JSON(http.StatusInternalServerError, gin.H{
"result": false,
"error_code": errorCode,
"msg": payload,
})
}
viper用來處理config的套件
簡單來說,它會把config.yaml的值會mapping到Config的struct裡面
config/config.go
// Config Config
type Config struct {
Mode string `mapstructure:"MODE"`
Port string `mapstructure:"PORT"`
}
// Init Init
func Init() {
viper.SetConfigName("config.yaml")
viper.SetConfigType("yaml")
viper.AddConfigPath(".")
if err := viper.ReadInConfig(); err != nil {
panic(fmt.Errorf("Fatal error config file: %v ", err))
}
if err := viper.Unmarshal(&Val); err != nil {
panic(fmt.Errorf("unable to decode into struct, %v", err))
}
log.WithFields(log.Fields{
"val": Val,
}).Info("config loaded")
}
config.yaml
MODE: "debug"
Port: 9090
為了方法開發,初期會有一些cross domain的問題,這邊有一個middleware 去處理
middleware/cross.go
func CROSS() gin.HandlerFunc {
return func(c *gin.Context) {
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
c.Writer.Header().Set("Access-Control-Allow-Origin", c.Request.Header.Get("origin"))
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With, Token, Set-Cookie, Cookie")
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, DELETE")
c.Writer.Header().Set("Access-Control-Allow-Max-Age", "1728000")
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}
c.Next()
}
}
main.go
func main() {
load()
gin.SetMode(config.Val.Mode)
r := gin.Default() // 預設會幫你掛上兩個middleware 1.access log 2.panic自動recovery
r.Use(middleware.CROSS())
r.GET("/ping", handler.Ping)
r.Run(":" + config.Val.Port)
log.Infof("serve port: %v \n", config.Val.Port)
}
func load() {
config.Init()
}
來啟動吧~
go run .
本機測試一下API
port是9090,docker-compose那邊有設定
看起來沒問題
先到這裡好累哦
明天會來講一下Googld OAuth的東西
謝謝大家!!