doilux’s tech blog

ITに関する備忘録。 DDP : http://doiluxng.hatenablog.com/entry/2018/01/01/195409

GoでWebアプリケーションを作ってみる_雛形を作ってみる

前回の続きで、GoでTwitterライクなWebアプリケーションを作ってみる。

もろもろライブラリを取得

WAFにはechoを使うことにしたのでechoを取得

go get -u github.com/labstack/echo/...

公式サイトの通りにやったらサンプル動いた。

次にecho-scaffoldを取得

doiluxng.hatenablog.com

プロジェクトの雛形を作る。

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だけでいい気がした。 以下参考。

github.com

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の指定の仕方で地味にハマった。

goのmgoでfindするときの処理 - Qiita

さらに追記

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}

イイね