前回の「Go言語 GORM+GinでTODOリストを作ってみた」に続いて「GORM+Gin」でTODOリストのAPIを作ってみました。ソースコードは前回からの差分だけを記載しています。できたものは下記URLから確認できます。 http://sandbox.taisablog.com/api/v1/
GinのGithub
事前情報
- 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" }
参考: https://github.com/hugomd/go-todo https://github.com/gin-gonic/gin 今回のソース: https://github.com/taisa831/gin-sandbox