此篇筆記適用於紀錄學習使用 go 作 web service 作練習。是參考這篇影片逐步練習和調整,會有點不一樣但值得參考。
初始化專案
$ go mod init gin-curd # 初始 go 專案模塊
$ go install github.com/cosmtrek/air@latest # 安裝監聽服務,類似 live reload 功能
$ go get -u github.com/gin-gonic/gin # 有名的 routing pkg
$ go get github.com/joho/godotenv # .env 環境變數 pkg
$ go get -u gorm.io/gorm # DB ORM
$ go get -u gorm.io/driver/mysql # mysql driver
設計
- Load .env variables
- Initial database connection and connected
- Definition routes
- create
- update
- destroy
- index (plural)
- show (singular)
實作
- controllers
- models
- migrations
- initializers (load .env and connect with database)
initializers/loadEnvVariables.go
package initializers
import (
"log"
"github.com/joho/godotenv"
)
func LoadEnvVariables() { // 確保大寫暴露使用接口
err := godotenv.Load()
if err != nil {
log.Fatal("Error load .env file.")
}
}
initializers/database.go
package initializers
import (
"log"
"os"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
var DB *gorm.DB
func Connection() {
var err error
dsn := os.Getenv("APP_CONNECTION") // 取得 .env 內的 APP_CONNECTION 變數
DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{}) //參考 gorm 文件 https://gorm.io/docs/connecting_to_the_database.html#MySQL
if err != nil {
log.Fatal("Connection failed.")
}
}
models/postModel.go
package models
import (
"time"
"gorm.io/plugin/soft_delete" // go mod tidy 抓 plugin pkg, 這邊我想自訂數據庫欄位順序
)
type Post struct {
ID uint `gorm:"primarykey"`
Title string
Body string
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt soft_delete.DeletedAt `gorm:"index"`
// gorm.Model, 或是可以方便使用這個
}
migrations/migate.go
package main
import (
"go-curd/initializers"
"go-curd/models"
)
func init() {
initializers.LoadEnvVariables() //載入環境變數
initializers.Connection() //建立連線
}
func main() {
initializers.DB.AutoMigrate(&models.Post{}) //參考文件 https://gorm.io/docs/migration.html#Auto-Migration
}
配置 environment variable,這邊要注意先建立 database:
.env
APP_CONNECTION=root@tcp(127.0.0.1:3306)/go-curd?charset=utf8mb4&parseTime=True&loc=Local
接著執行進行 migrate DB schema:
$ go run migrations/migrate.go
controllers/postController.go
package controllers
import (
"errors"
"go-curd/initializers"
"go-curd/models"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
func PostCreate(c *gin.Context) {
var content struct { // 宣告接收傳入參數
Title string
Body string
}
c.Bind(&content)
post := models.Post{Title: content.Title, Body: content.Body} // model 指定寫入
result := initializers.DB.Create(&post) // 建立
if result.Error != nil {
c.Status(400)
return
}
c.JSON(200, gin.H{
"post": post,
})
}
func PostIndex(c *gin.Context) {
var posts []models.Post
initializers.DB.Find(&posts)
c.JSON(200, gin.H{
"posts": posts,
})
}
func PostShow(c *gin.Context) {
id := c.Param("id") // 接收 uri 參數
var post models.Post
if err := initializers.DB.First(&post, id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { //如果錯誤訊息是 not found 則出現 post not found json 回應訊息
c.JSON(404, gin.H{
"error": "Post not found",
})
return
}
c.JSON(422, gin.H{
"error": err,
})
}
c.JSON(200, gin.H{
"post": post,
})
}
func PostUpdate(c *gin.Context) {
id := c.Param("id")
var content struct {
Title string
Body string
}
c.Bind(&content)
var post models.Post
if err := initializers.DB.First(&post, id).Error; err != nil { //取得 post
if errors.Is(err, gorm.ErrRecordNotFound) {
c.JSON(404, gin.H{
"error": "Post not found",
})
return
}
c.JSON(422, gin.H{
"error": err,
})
}
initializers.DB.Model(&post).Updates(models.Post{ //更新 &post
Title: content.Title,
Body: content.Body,
})
c.JSON(200, gin.H{
"post": post,
})
}
func PostDestory(c *gin.Context) {
id := c.Param("id")
initializers.DB.Delete(&models.Post{}, id)
c.Status(200)
}
main.go
package main
import (
"go-curd/initializers"
"go-curd/controllers"
"github.com/gin-gonic/gin"
)
func init() {
initializers.LoadEnvVariables()
initializers.Connection()
}
func main() {
r := gin.Default()
r.POST("/posts", controllers.PostCreate)
r.GET("/posts", controllers.PostIndex)
r.GET("/posts/:id", controllers.PostShow)
r.PUT("/posts/:id", controllers.PostUpdate)
r.DELETE("/posts/:id", controllers.PostDestory)
r.Run()
}
Running
go run main.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] POST /posts --> go-curd/controllers.PostCreate (3 handlers)
[GIN-debug] GET /posts --> go-curd/controllers.PostIndex (3 handlers)
[GIN-debug] GET /posts/:id --> go-curd/controllers.PostShow (3 handlers)
[GIN-debug] PUT /posts/:id --> go-curd/controllers.PostUpdate (3 handlers)
[GIN-debug] DELETE /posts/:id --> go-curd/controllers.PostDestory (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
[GIN-debug] Environment variable PORT is undefined. Using port :8080 by default
[GIN-debug] Listening and serving HTTP on :8080