前回の続きで、GoでTwitterライクなWebアプリケーションを作ってみる。
もろもろライブラリを取得
WAFにはechoを使うことにしたのでechoを取得
go get -u github.com/labstack/echo/...
公式サイトの通りにやったらサンプル動いた。
次にecho-scaffoldを取得
プロジェクトの雛形を作る。
echo-scaffold init my-go-microblog-sample cd my-go-microblog-sample export PROJECT_ROOT=`pwd` // $PROJECT_ROOTとする
$PROJECT_ROOT/my-go-microblog-sample.goを動かしてみると「github.com/globalsign/mgoがねーぞ」というエラーになる。 github.com/globalsign/mgoをgetして、再実行。
go get github.com/globalsign/mgo
go run my-go-microblog-sample.go
____ __
/ __/___/ / ___
/ _// __/ _ \/ _ \
/___/\__/_//_/\___/ v3.3.dev
High performance, minimalist Go web framework
https://echo.labstack.com
____________________________________O/_______
O\
⇨ http server started on [::]:4000
動いた。 確認:http://localhost:4000/
Tweetエンティティを生成してみる
tweetを作ってみる。microblogってアプリケーションにしたのでarticleの方が正しいかもしれないけど、まあいいや。
echo-scaffold scaffold tweet body:string postAt:time
create models/tweet.go
panic: 23:10: expected ';', found 'IDENT' int64 (and 3 more errors)
goroutine 1 [running]:
github.com/mattn/echo-scaffold/template.(*Builder).Write(0xc42009a3c0, 0x11e9ac0, 0xc4200946c0, 0x11985e0, 0xc4200ec4d0)
/Users/masahiro/go/src/github.com/mattn/echo-scaffold/template/builder.go:104 +0x2ce
github.com/mattn/echo-scaffold/template.(*Builder).WriteToPath(0xc42009a3c0, 0xc4200865a0, 0xf, 0x11985e0, 0xc4200ec4d0)
/Users/masahiro/go/src/github.com/mattn/echo-scaffold/template/builder.go:126 +0x15c
github.com/mattn/echo-scaffold/command.(*ModelCommand).Execute(0xc4200ec4d0, 0xc420096110, 0x3, 0x3)
/Users/masahiro/go/src/github.com/mattn/echo-scaffold/command/model_command.go:90 +0x3a0
github.com/mattn/echo-scaffold/command.(*ScaffoldCommand).Execute(0x12d4140, 0xc420096110, 0x3, 0x3)
/Users/masahiro/go/src/github.com/mattn/echo-scaffold/command/scaffold_command.go:41 +0x17e
main.main()
/Users/masahiro/go/src/github.com/mattn/echo-scaffold/echo-scaffold.go:61 +0x10c
ん?これはうまくいったのか?もう一回叩いてみる。
echo-scaffold scaffold tweet body:string postAt:time
create models/tweet.go
skip models/tweet.go
create models/tweet_dbsession.go
create controllers/tweets.go
create controllers/tweets_helpers.go
create controllers/suite_test.go
skip controllers/suite_test.go
insert controllers/router.go
生成されたコードをちょっと読んでみる。
func Setup(router *echo.Router) {
tweets := TweetsController{Router: router}
tweets.Setup()
}
router.goでnewしてるTweetsControllerの定義
type TweetsController struct {
// 埋め込み型。TweetsControllerがRouter型を内包
Router *echo.Router
}
routerから呼ばれているSetup
func (controller *TweetsController) Setup() {
controller.Router.Add("POST", "/tweets", controller.createTweet)
controller.Router.Add("GET", "/tweets", controller.listTweets)
controller.Router.Add("GET", "/tweets/:tweet_id", controller.getTweet)
controller.Router.Add("PUT", "/tweets/:tweet_id", controller.updateTweet)
controller.Router.Add("DELETE", "/tweets/:tweet_id", controller.deleteTweet)
}
createTweetメソッド
func (controller *TweetsController) createTweet(c echo.Context) error {
c.Request().ParseForm()
tweet := &models.Tweet{}
tweet.SetAttributes(c.Request().Form)
tweet.Save()
if len(tweet.ErrorMessages()) == 0 {
return helpers.JSONResponseObject(c, 200, tweet)
}
return helpers.JSONResponse(c, 400, tweet.ErrorMessages())
}
なぜかgoland先生が&models.Tweet{}のところで警告出してると思ったら、models/tweet.goは空かい。
config/database.goにはデータストアの定義がある。 デフォルトはmongodbらしい。
func DBSession() *mgo.Session {
if mongodbSession != nil {
return mongodbSession
}
uri := os.Getenv("MONGODB_URI")
if uri == "" {
uri = "mongodb://localhost"
}
...
config/environment.goには環境に関する設定があって、デフォルトはdevelopment環境とな。
func Setup(e *echo.Echo) {
if Environment == "" {
Environment = "development"
}
DefaultDBName = dbPrefix + "-" + Environment
...
最後に、この状態でgo runしようとすると「github.com/sirupsen/logrusがねーぞ」というエラーになるのでgetする。
go get github.com/sirupsen/logrus
再度go runしようとするとエラーになる。 まあ、そらそうね。
go run my-go-microblog-sample.go controllers/tweets.go:7:2: models/tweet.go:1:1: expected 'package', found 'EOF'
一旦ここまで、次回に続く。
追記
tweet.goが空だったのはscaffoldコマンドをミスっているんじゃないかという気がした。 postAtのところは、うまくいけばCreatedAtという属性値ができるので、bodyだけでいい気がした。 以下参考。
scaffoldを再実行する。
echo-scaffold scaffold tweet body:string
create models/tweet.go
create models/tweet_dbsession.go
create controllers/tweets.go
create controllers/tweets_helpers.go
create controllers/suite_test.go
skip controllers/suite_test.go
insert controllers/router.go
tweet.goが空じゃない。
...
type Tweet struct {
ID bson.ObjectId `bson:"_id,omitempty"`
Body string `bson:"body"`
CreatedAt int64 `bson:"created_at"`
UpdatedAt int64 `bson:"updated_at"`
Errors helpers.Errors `bson:"-"`
}
...
日時はint64で表現するのか??
go runしてみると起動した。でもきっとmongodbに繋がらないのでエラーになるはず。
go run my-go-microblog-sample.go
____ __
/ __/___/ / ___
/ _// __/ _ \/ _ \
/___/\__/_//_/\___/ v3.3.dev
High performance, minimalist Go web framework
https://echo.labstack.com
____________________________________O/_______
O\
⇨ http server started on [::]:4000
うん、なんの応答もない。
curl http://localhost:4000/tweets/565edd868bc93d268a13bc02
ちなみにIDの指定の仕方で地味にハマった。
さらに追記
mongo立てるのめんどくせーのでdockerで
https://hub.docker.com/r/_/mongo/
docker pull mongo docker run --name my-mongo -p 27017:27017 mongo
参考:telnetでポートの解放確認
telnet telnet> open localhost 27017 Trying ::1... Connected to localhost.
この状態でgo runしPOSTしてみる
curl -F "body=hello" http://localhost:4000/tweets
{"body":"","id":"5aea96a541d1c2812b7daadf","success":true}
いい感じ。 mongodbの中を見てみる。
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f33be02d0309 mongo "docker-entrypoint.s…" 2 minutes ago Up 2 minutes 0.0.0.0:27017->27017/tcp my-mongo
docker exec -it f33be02d0309 /bin/bash
root@f33be02d0309:/# mongo
> show dbs;
admin 0.000GB
config 0.000GB
local 0.000GB
my-go-microblog-sample-development 0.000GB
> use my-go-microblog-sample-development
switched to db my-go-microblog-sample-development
> show collections;
tweets
> db.tweets.find()
{ "_id" : ObjectId("5aea96a541d1c2812b7daadf"), "body" : "", "created_at" : NumberLong(1525323429), "updated_at" : NumberLong(0) }
あれ?body入ってない??
curl http://localhost:4000/tweets/5aea96a541d1c2812b7daadf
{"body":"","id":"5aea96a541d1c2812b7daadf","success":true}
Loggerを仕込んでみる
func (controller *TweetsController) createTweet(c echo.Context) error {
c.Request().ParseForm()
// logging form
log.Print(c.Request().Form)
tweet := &models.Tweet{}
tweet.SetAttributes(c.Request().Form)
tweet.Save()
if len(tweet.ErrorMessages()) == 0 {
return helpers.JSONResponseObject(c, 200, tweet)
}
return helpers.JSONResponse(c, 400, tweet.ErrorMessages())
}
空だった
2018/05/03 14:20:19 map[]
となるとcurlコマンドが間違ってんじゃね?とこうしてみる
curl http://localhost:4000/tweets -X POST -d "body=hello"
{"body":"hello","id":"5aea9ca941d1c2832559a2fe","success":true}
ログ
2018/05/03 14:22:49 map[body:[hello]]
DB
> db.tweets.find()
{ "_id" : ObjectId("5aea96a541d1c2812b7daadf"), "body" : "", "created_at" : NumberLong(1525323429), "updated_at" : NumberLong(0) }
{ "_id" : ObjectId("5aea9c1341d1c2832559a2fd"), "body" : "", "created_at" : NumberLong(1525324819), "updated_at" : NumberLong(0) }
{ "_id" : ObjectId("5aea9ca941d1c2832559a2fe"), "body" : "hello", "created_at" : NumberLong(1525324969), "updated_at" : NumberLong(0) }
うまくいったっぽい。最後にGET
curl http://localhost:4000/tweets/5aea9ca941d1c2832559a2fe
{"body":"hello","id":"5aea9ca941d1c2832559a2fe","success":true}
イイね