前回 CLI で記事を追加する機能を追加しました。 今回は SERVER から記事一覧と記事詳細を取得する処理を追加します。
取り急ぎ JSON を返す処理を追加する
edit server/router.go:
func AddRoute(r *gin.Engine, dbmap *gorp.DbMap) *gin.Engine { r.GET("/ping", func(c *gin.Context) { c.String(200, "pong") }) r.GET("/", func(c *gin.Context) { c.JSON(200, gin.H{ "status": "posted", "message": "message", }) }) return r }
edit server/router_test.go: 引数に dbmap を追加したので修正する
package main import ( "github.com/coopernurse/gorp" "github.com/gin-gonic/gin" "github.com/okamuuu/gin-tutorial/shared" "github.com/stretchr/testify/assert" "io/ioutil" "net/http" "testing" "time" ) func testRequest(t *testing.T, url string) { resp, err := http.Get(url) defer resp.Body.Close() assert.NoError(t, err) body, ioerr := ioutil.ReadAll(resp.Body) assert.NoError(t, ioerr) assert.Equal(t, "pong", string(body), "resp body should match") assert.Equal(t, "200 OK", resp.Status, "should get a 200") } func TestRun(t *testing.T) { var dsn string = "root:@tcp(127.0.0.1:3306)/test_db" dbmap := shared.NewDbMap(dsn) shared.CreateTablesIfNotExists(dbmap) defer dropAndClose(dbmap) router := gin.New() AddRoute(router, dbmap) go func() { assert.NoError(t, router.Run()) }() // have to wait for the goroutine to start and run the server // otherwise the main thread will complete time.Sleep(5 * time.Millisecond) assert.Error(t, router.Run(":8080"), "already running") testRequest(t, "http://localhost:8080/ping") } func dropAndClose(dbmap *gorp.DbMap) { dbmap.DropTablesIfExists() dbmap.Db.Close() }
動作確認
% go test router* ok command-line-arguments 0.048s
実装
package main import ( "database/sql" "github.com/coopernurse/gorp" "github.com/gin-gonic/gin" "github.com/okamuuu/gin-tutorial/shared" "log" ) func AddRoute(r *gin.Engine, dbmap *gorp.DbMap) *gin.Engine { r.GET("/ping", func(c *gin.Context) { c.String(200, "pong") }) r.GET("/articles", func(c *gin.Context) { var articles []shared.Article _, err := dbmap.Select(&articles, "select title, created from articles order by id desc") if err != nil { log.Println("DB Error: ", err) c.String(500, "Internal Server Error") return } content := make([]interface{}, len(articles)) for i, article := range articles { content[i] = map[string]interface{}{"title": article.Title, "created": article.Created} } c.JSON(200, content) }) r.GET("/articles/:id", func(c *gin.Context) { id := c.Param("id") var article shared.Article err := dbmap.SelectOne(&article, "select * from articles where id=?", id) if err == sql.ErrNoRows { c.String(404, "Not Found") return } else if err != nil { log.Println("DB Error: ", err) c.String(500, "Internal Server Error") return } c.JSON(200, article) }) return r }
動作確認
% go run main.go router.go
curl で確認します。DBに適当なデータをインサートしておいてください。
% curl -s localhost:8080/articles | xargs echo [{created:1440587591,title:this is markdown},{created:1440587072,title:this is markdown},{created:1440562341,title:this is markdown},{created:1440561336,title:this is markdown},{created:1440561201,title:this is markdown},{created:1440468516,title:Go is awesome},{created:1440468506,title:go},{created:1440468369,title:title2},{created:1440468360,title:title}] % curl -s localhost:8080/articles/7 | xargs echo {id:7,title:this is markdown,desc:outline\n\n## header 2\n\nparagraph\n,created:1440562341,published:0}
以上です。
おわりに
最初は gin の tutorial を書こうと思ったのですが、題材が微妙だったせいか、全く gin の tutorial になってない結末になってしまいました。
とりあえずエラーハンドリングが適当だったのと interface{} や string <=> []byte あたりを少し補足したほうがいいかも。
とりあえず一旦寝かせて Go の知見が増えたらまとめて github に公開しようとおもいます。