前回の「Go言語 GORM+GinでTODOリストを作ってみた」に続いて「GORM+Gin」でTODOリストのAPIを作ってみました。
事前情報
- Webフレームワーク:Gin (https://github.com/gin-gonic/gin)
- ORM:GORM (https://gorm.io/docs)
- DB:MySQL
ルーティングは今回はAPI
なので以下としました。モデルをtasks
にすればよかったと思いましたが一旦このままにしておきます。
[GIN-debug] GET /todo --> main.main.func1 (3 handlers) // 一覧表示
[GIN-debug] POST /todo --> main.main.func2 (3 handlers) // 新規作成
[GIN-debug] GET /todo/:id --> main.main.func3 (3 handlers) // 編集画面表示
[GIN-debug] PUT /todo/:id --> main.main.func4 (3 handlers) // 編集
[GIN-debug] DELETE /todo/:id --> main.main.func5 (3 handlers) // 削除
ディレクトリ構成
.
├──api
│ └── v1
│ └── todo.go
├── controllers
│ └── todo.go
├── db
│ └── db.go
├── main.go
├── models
│ └── todo.go
├── router
└── router.go
router.go
router.go
にr.Group("/api/v1")
のAPI用のグループを追加してルーティングを追加しました。
package router
import (
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
v1 "github.com/taisa831/sandbox-gin/api/v1"
"github.com/taisa831/sandbox-gin/controllers"
"time"
)
func Router(dbConn *gorm.DB) {
todoHandler := controllers.TodoHandler{
Db: dbConn,
}
r := gin.Default()
r.Use(cors.New(cors.Config{
AllowOrigins: []string{"*"},
AllowMethods: []string{"PUT", "PATCH", "DELETE", "POST", "GET"},
AllowHeaders: []string{"Origin"},
ExposeHeaders: []string{"Content-Length"},
AllowCredentials: true,
AllowOriginFunc: func(origin string) bool {
return origin == "*"
},
MaxAge: 12 * time.Hour,
}))
r.LoadHTMLGlob("templates/*")
r.GET("/todo", todoHandler.GetAll) // 一覧画面
r.POST("/todo", todoHandler.CreateTask) // 新規作成
r.GET("/todo/:id", todoHandler.EditTask) // 編集画面
r.POST("/todo/edit/:id", todoHandler.UpdateTask) // 更新
r.POST("/todo/delete/:id", todoHandler.DeleteTask) // 削除
apiV1 := r.Group("/api/v1")
{
apiTodoHandler := v1.TodoHandler{
Db: dbConn,
}
apiV1.GET("/todo", apiTodoHandler.GetAll) // 一覧画面
apiV1.POST("/todo", apiTodoHandler.CreateTask) // 新規作成
apiV1.GET("/todo/:id", apiTodoHandler.EditTask) // 編集画面
apiV1.PUT("/todo/:id", apiTodoHandler.UpdateTask) // 更新
apiV1.DELETE("/todo/:id", apiTodoHandler.DeleteTask) // 削除
}
r.Run(":9000")
}
api/v1/todo.go
JSON
で受けた値を処理してJSON
を返すように変更しました。
package v1
import (
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
"github.com/taisa831/gin-sandbox/models"
"net/http"
)
type TodoHandler struct {
Db *gorm.DB
}
func (h *TodoHandler) GetAll(c *gin.Context) {
var todos []models.Todo
h.Db.Find(&todos)
c.JSON(http.StatusOK, todos)
}
func (h *TodoHandler) CreateTask(c *gin.Context) {
todo := models.Todo{}
err := c.BindJSON(&todo)
if err !=nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
return
}
h.Db.Create(&todo)
c.JSON(http.StatusOK, &todo)
}
func (h *TodoHandler) EditTask(c *gin.Context) {
todo := models.Todo{}
id := c.Param("id")
h.Db.First(&todo, id)
c.JSON(http.StatusOK, todo)
}
func (h *TodoHandler) UpdateTask(c *gin.Context) {
todo := models.Todo{}
id := c.Param("id")
h.Db.First(&todo, id)
err := c.BindJSON(&todo)
if err !=nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
}
h.Db.Save(&todo)
c.JSON(http.StatusOK, &todo)
}
func (h *TodoHandler) DeleteTask(c *gin.Context) {
todo := models.Todo{}
id := c.Param("id")
h.Db.First(&todo, id)
err := h.Db.First(&todo, id).Error
if err !=nil {
c.AbortWithStatus(http.StatusNotFound)
return
}
h.Db.Delete(&todo)
c.JSON(http.StatusOK, gin.H{
"status": "ok",
})
}
動作確認
ターミナルでjson
をみやすくするようにjsonpp
を入れておきます。
brew install jsonpp
一覧取得
% curl -X GET -H "Content-Type: application/json"http://localhost:9000/api/v1/todo | jsonpp
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 271 100 271 0 0 44172 0 --:--:-- --:--:-- --:--:-- 45166
[
{
"ID": 1,
"CreatedAt": "2019-07-04T10:16:28+09:00",
"UpdatedAt": "2019-07-04T10:16:28+09:00",
"DeletedAt": null,
"Text": "テスト",
"Status": 1
},
{
"ID": 2,
"CreatedAt": "2019-07-04T10:16:38+09:00",
"UpdatedAt": "2019-07-04T10:16:38+09:00",
"DeletedAt": null,
"Text": "実装",
"Status": 1
}
]
新規作成
% curl -X POST -H "Content-Type: application/json" -d '{"text":"test", "status":2}'http://localhost:9000/api/v1/todo | jsonpp
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 172 100 145 100 27 23592 4393 --:--:-- --:--:-- --:--:-- 24166
{
"ID": 3,
"CreatedAt": "2019-07-04T10:18:45.041387+09:00",
"UpdatedAt": "2019-07-04T10:18:45.041387+09:00",
"DeletedAt": null,
"Text": "test",
"Status": 2
}
更新
% curl -X PUT -H "Content-Type: application/json" -d '{"text":"update", "status":3}'http://localhost:9000/api/v1/todo/1 | jsonpp
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 169 100 140 100 29 20951 4340 --:--:-- --:--:-- --:--:-- 23333
{
"ID": 1,
"CreatedAt": "2019-07-04T10:16:28+09:00",
"UpdatedAt": "2019-07-04T10:19:45.818126+09:00",
"DeletedAt": null,
"Text": "update",
"Status": 3
}
削除
% curl -X DELETE -H "Content-Type: application/json"http://localhost:9000/api/v1/todo/1 | jsonpp
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 16 100 16 0 0 2025 0 --:--:-- --:--:-- --:--:-- 2285
{
"status": "ok"
}