練習題 3

step 0. install

$ go get -u github.com/gin-gonic/gin
$ go mod vendor

step 1. Gin basic

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() // @ 0.0.0.0:8080
}

step 2. Prototype (without db)

|- main.go
|- /todos
        |- models.go
        |- routers.go

main.go

package main

import (
    "github.com/JasonYCHuang/todo/todos"
    "github.com/gin-gonic/gin"
)

func main() {
    r := gin.Default()

    api := r.Group("/api")
    v1 := api.Group("/v1")
    todosRegister := v1.Group("/todos")
    {
        todosRegister.GET("/", todos.GetTodosHandler)
        todosRegister.GET("/:id", todos.GetTodoHandler)
        todosRegister.POST("/", todos.CreateTodoHandler)
        todosRegister.PUT("/:id", todos.UpdateTodoHandler)
        todosRegister.DELETE("/:id", todos.DeleteTodoHandler)
    }

    r.Run(":3001")
}

todos/routers.go

package todos

import (
    "github.com/gin-gonic/gin"
    "net/http"
)

func GetTodosHandler(c *gin.Context) {
    tds, err := findAllTodos()
    if err != nil {
        c.AbortWithStatus(404)
        return
    }
    c.JSON(http.StatusOK, gin.H{"todos": tds})
}

func GetTodoHandler(c *gin.Context) {
    id := c.Param("id")

    td, err := findOneTodo(id)
    if err != nil {
        c.AbortWithStatus(404)
        return
    }
    c.JSON(http.StatusOK, gin.H{"todo": td})
}

func CreateTodoHandler(c *gin.Context) {
    var td TodoModel

    c.BindJSON(&td)
    td.Create()

    c.JSON(http.StatusOK, gin.H{"todos": td})
}

func UpdateTodoHandler(c *gin.Context) {
    id := c.Param("id")

    td, err := findOneTodo(id)
    if err != nil {
        c.AbortWithStatus(404)
        return
    }

    c.BindJSON(&td)
    if err = td.Update(); err != nil {
        c.AbortWithStatus(404)
        return
    }

    c.JSON(http.StatusOK, gin.H{"todo": td})
}

func DeleteTodoHandler(c *gin.Context) {
    id := c.Param("id")

    td, err := findOneTodo(id)
    if err != nil {
        c.AbortWithStatus(404)
        return
    }

    td.Delete()
    c.JSON(http.StatusOK, gin.H{"todo": td})
}

todos/models.go

package todos

import (
    "errors"
    "strconv"
)

type TodoModel struct {
    ID    int
    Title string
}

var store []TodoModel
var inc int

func findAllTodos() (tds []TodoModel, err error) {
    tds = store
    return
}

func findOneTodo(id string) (td TodoModel, err error) {
    if iid, err := strconv.Atoi(id); err == nil {
        for _, v := range store {
            if v.ID == iid {
                return v, err
            }
        }
    }
    err = errors.New("Todo not found!")
    return
}

func (td *TodoModel) Create() {
    td.ID = inc
    inc++
    store = append(store, *td)
}

func (td *TodoModel) Update() (err error) {
    counter := 0
    for _, v := range store {
        if v.ID == td.ID {
            break
        }
        counter++
    }
    store = append(
        store[:counter],
        append([]TodoModel{*td}, store[counter+1:]...)...,
    )
    return
}

func (td *TodoModel) Delete() {
    counter := 0
    for _, v := range store {
        if v.ID == td.ID {
            break
        }
        counter++
    }
    store = append(store[:counter], store[counter+1:]...)
}

step 3. handler register

main.go

func main() {
    ...
    api := r.Group("/api")
    v1 := api.Group("/v1")
    todos.TodosRegister(v1.Group("/todos"))
    ...
}

router.go

func TodosRegister(router *gin.RouterGroup) {
    router.GET("/", getTodosHandler)
    router.GET("/:id", getTodoHandler)
    router.POST("/", createTodoHandler)
    router.PUT("/:id", updateTodoHandler)
    router.DELETE("/:id", deleteTodoHandler)
}

step 4. serializer

serializers.go

package todos

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

type TodoSerializer struct {
    C *gin.Context
    TodoModel
}

type TodosSerializer struct {
    C          *gin.Context
    TodoModels []TodoModel
}

type TodoResponse struct {
    ID    int    `json:"id"`
    Title string `json:"title"`
}

func (s *TodoSerializer) Response() (rsp TodoResponse) {
    m := s.TodoModel
    rsp = TodoResponse{
        ID:    m.ID,
        Title: m.Title,
    }
    return
}

func (s *TodosSerializer) Response() (rsp []TodoResponse) {
    for _, v := range s.TodoModels {
        serializer := TodoSerializer{s.C, v}
        rsp = append(rsp, serializer.Response())
    }
    return
}

routers.go

func getTodosHandler(c *gin.Context) {
    ...
    serializer := TodosSerializer{c, tds}
    c.JSON(http.StatusOK, gin.H{"todos": serializer.Response()})
}

step 5. gorm

main.go

package main

import (
    "github.com/JasonYCHuang/todo/common"
    "github.com/gin-gonic/gin"
    "github.com/jinzhu/gorm"
)

func Migrate(db *gorm.DB) {
    db.AutoMigrate(&todos.TodoModel{})
}

func main() {
    db := common.Init()
    Migrate(db)
    defer db.Close()

    ...
}

todos/model.go

package todos

import (
    "github.com/JasonYCHuang/todo/common"
    "github.com/jinzhu/gorm"
    "strconv"
)

type TodoModel struct {
    gorm.Model
    Title string
}

func findAllTodos() (tds []TodoModel, err error) {
    db := common.GetDB()
    err = db.Find(&tds).Error
    return
}

func findOneTodo(id string) (td TodoModel, err error) {
    maps := make(map[string]interface{})
    if idx, err := strconv.Atoi(id); err == nil {
        maps["id"] = idx
    }
    err = td.Get(maps)
    return
}

func (td *TodoModel) Get(maps interface{}) (err error) {
    db := common.GetDB()
    err = db.Where(maps).First(td).Error
    return
}

func (td *TodoModel) Create() (err error) {
    db := common.GetDB()
    err = db.Create(td).Error
    return
}

func (td *TodoModel) Update() (err error) {
    db := common.GetDB()
    err = db.Save(td).Error
    return
}

func (td *TodoModel) Delete() (err error) {
    db := common.GetDB()
    err = db.Delete(td).Error
    return
}

common/database.go

package common

import (
    "fmt"
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/sqlite"
    "os"
)

type Database struct {
    *gorm.DB
}

var DB *gorm.DB

func Init() *gorm.DB {
    db, err := gorm.Open("sqlite3", "./gorm.db")
    if err != nil {
        fmt.Println("db err: ", err)
    }
    db.DB().SetMaxIdleConns(10)
    //db.LogMode(true)
    DB = db
    return DB
}

func GetDB() *gorm.DB {
    return DB
}

Last updated

Was this helpful?