我們會分成兩個章節來調整 To-do List 專案架構,主要是將「 有相關聯、會互相影響 」的程式做區分:
第一部分:service、model、middleware、main
第二部分:handler、repository、service
所以,今天會先從第一部分開始介紹!
大家要記得先把 To-do List 專案的檔案建立好!可以對照上一個章節的示意圖,名稱可以自己命名。
完成檔案建置後,就可以移動程式了~
一開始先來定義 task 資料結構。把會使用到的名稱都先定義好,並且綁定 json tag。
package model
type Task struct {
ID int `json:"id"`
Item string `json:"item"`
Status bool `json:"status"`
}
type UpdateTask struct { // PATCH,用指標判斷是否有傳欄位
Item *string `json:"item"`
Status *bool `json:"status"`
}
然後建立 Error massage 的錯誤定義,這部分會傳入 middleware 做使用。另外, Error message 要記得使用英文字母小寫唷!除了 Error message 之外,service 檔案裡面還會放 CRUD 的流程,這部分明天再一起介紹。
package taskService
import (
"errors"
)
// Error messages
var (
ErrNotFound = errors.New("task not found")
ErrInvalidInput = errors.New("input could not be null")
ErrInvalidID = errors.New("invalid id")
)
建立回傳的 Error massage。這邊的 Error message 會呼叫剛剛在 service 檔案裡面定義的錯誤(taskService.~
)。然後當我們呼叫時這個函式時,就會自動找出要回應的對應錯誤內容。
package middleware
import (
"errors"
"net/http"
"app/service"
"github.com/gin-gonic/gin"
)
func ErrorHandler() gin.HandlerFunc {
return func(c *gin.Context) {
c.Next()
if len(c.Errors) == 0 {
return
}
err := c.Errors.Last().Err
var status int
var msg string
switch {
// 帶入剛剛在 service 定義的錯誤訊息
case errors.Is(err, taskService.ErrNotFound):
status = http.StatusNotFound
msg = err.Error()
case errors.Is(err, taskService.ErrInvalidID):
status = http.StatusBadRequest
msg = err.Error()
case errors.Is(err, taskService.ErrInvalidInput):
status = http.StatusBadRequest
msg = err.Error()
default:
status = http.StatusInternalServerError
msg = "internal server error"
}
c.AbortWithStatusJSON(status, gin.H{"error": msg})
}
}
最後,main.go
主要就是放 啟動 server 和一些路由的設置。這部分需要留意的地方是建立路由 r.GET("/tasks", apiHandler.GetTasks)
的部分,這一個 apiHandler.GetTasks
是對應到 apiHandler 裡面的 function,所以函式名稱記得要大寫。
package main
import (
"app/apiHandler"
"app/middleware"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.Use(middleware.ErrorHandler())
r.GET("/tasks", apiHandler.GetTasks)
r.POST("/tasks", apiHandler.AddTask)
r.PATCH("/tasks/:id", apiHandler.UpdateTask)
r.DELETE("/tasks/:id", apiHandler.DeleteTask)
r.Run(":8080")
}
明天會再繼續把剩下的部分完善!