スキップしてメイン コンテンツに移動

Goa Hello World

この章では、goaを使ってHello World!と表示させるAPIのサンプルを作ってみます。
何となくgoaとはどういうものか、感覚が掴めると思います。

セットアップ

サンプルを作る土台作りをしましょう。
  • gvm
  • golang 1.7.5
  • goa

の3つをインストールします。
※既にインストールしている場合は読み飛ばしてください

gvmをインストール

サンプルはGoのバージョン1.7.5を使って作っていきます。
 それではまず、Goのバージョン管理ツールであるgvmをインストールしましょう。

$ bash < <(curl -s -S -L https://raw.githubusercontent.com/moovweb/gvm/master/binscripts/gvm-installer)
$ source ~/.gvm/scripts/gvm
$ gvm version

これで、gvmのバージョンが表示されたら成功です。

Goをインストール

gvmを使ってGoをインストールします。
通常、gvmを使ってGoをインストールする場合は

$ gvm install go1.7.5

で良いのですが、Mac OSがSierra以上だと、コンパイルが失敗します。
このブログを執筆している時点での話なので、そのうち解消しているかも知れません

なので-Bオプションを付けて、バイナリのみをインストールします。

$ gvm install go1.7.5 -B
$ gvm use go1.7.5
$ go vesion

Goのバージョンが表示されたら成功です。

goaのインストール



$ go get -u github.com/goadesign/goa/...
$ goagen version

goagenのバージョンが表示されたら成功です。

実装

セットアップが完了したところで、goaを使ってアプリケーションのデザインをしていきます。
 作業ディレクトリはgoasampleとしましょう。

$ cd $GOPATH/src
$ mkdir goasample
$ cd goasample

また、goaを進めるに当たって、
  • 自分が何のファイルを実装する事になるのか
  • どういうファイルが自動で作られるのか
の変遷を見るために、gitで管理していきましょう。

$ git init

design/design.go

goaのAPIデザインを定義していくデザインファイルを作成します。

$ mkdir design
$ vim design/design.go

そして、以下のソースコードを実装します。

package design

import (
    . "github.com/goadesign/goa/design"
    . "github.com/goadesign/goa/design/apidsl"
)

var _ = API("goasample", func() {
    Title("Hello World API")
    Description("goaでハローワールドを表示するサンプルアプリケーションです")
    Host("localhost:8080")
    Scheme("http")
})

var _ = Resource("helloworld", func() {
    Action("show", func() {
        Routing(GET(""))
        Description("ハローワールドを表示します")
        Response(OK, "text/plain")
    })
})

ここで一旦、ソースコードをgitのstageにあげましょう。

$ git add .
$ git status
On branch master

Initial commit

Changes to be committed:
  (use "git rm --cached ..." to unstage)

        new file:   design/design.go

コード生成

デザインファイルができたら、そこからGoのソースコードを生成します。

goagen bootstrap -d goasample/design

goagenで、様々なファイルが作られたと思います。

$ git status
On branch master

Initial commit

Changes to be committed:
  (use "git rm --cached ..." to unstage)

        new file:   design/design.go

Untracked files:
  (use "git add ..." to include in what will be committed)

        app/
        client/
        helloworld.go
        main.go
        swagger/
        tool/

  • app/
  • client/
  • helloworld.go
  • main.go
  • swagger/
  • tool/

という、4つのディレクトリと、2つのファイルが生成されましたね。
それぞれの意味は、次章にて説明していきます。

ビルド

ここまできたら、もうビルドできる状態です。

$ go build

するとgoasampleという成果物(実行ファイル)が生成されました。
実行してみましょう。

$ ./goasample
2017/05/28 17:11:53 [INFO] mount ctrl=Helloworld action=Show route=GET /
2017/05/28 17:11:53 [INFO] listen transport=http addr=:8080

ポート8080でlistenしてますね。
curlでアクセスしてみましょう

$ curl "http://localhost:8080"

何も表示されていませんね。
ビジネスロジックを何も書いていないため、レスポンスボディは空です。
しかし、goasampleは何やら、ログを出力しています。

2017/05/28 17:11:53 [INFO] mount ctrl=Helloworld action=Show route=GET /
2017/05/28 17:11:53 [INFO] listen transport=http addr=:8080
2017/05/28 17:11:59 [INFO] started req_id=w7jBWswQUi-1 GET=/ from=::1 ctrl=HelloworldController action=show
2017/05/28 17:11:59 [INFO] headers req_id=w7jBWswQUi-1 User-Agent=curl/7.51.0 Accept=*/*
2017/05/28 17:11:59 [INFO] completed req_id=w7jBWswQUi-1 status=0 bytes=0 time=130.291µs ctrl=HelloworldController action=show

アクセスがあったように見えます。
疎通はできているようです。
しかし、これだけじゃAPIが作られているのか不安になりますので、 curl -v でHTTPヘッダを確認してみましょう。

$ curl -v "http://localhost:8080"
* Rebuilt URL to: http://localhost:8080/
*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> GET / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.51.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Sun, 28 May 2017 08:15:05 GMT
< Content-Length: 0
< Content-Type: text/plain; charset=utf-8
<
* Curl_http_done: called premature == 0
* Connection #0 to host localhost left intact

HTTP/1.1 200 OKと返ってきていればAPIの疎通確認としては十分です。

ビジネスロジックの実装

レスポンスボディを返すため、ロジックを実装します。
変化を見るため、またソースコードをgitのstageに追加しましょう。

$ git add .
$ vim helloworld.go

helloworld.goを開くと、以下のメソッドがあると思います。

// Show runs the show action.
func (c *HelloworldController) Show(ctx *app.ShowHelloworldContext) error {
    // HelloworldController_Show: start_implement

    // Put your logic here

    // HelloworldController_Show: end_implement
    return nil
}

先ほどのcurlでアクセスした時、ここのメソッドが呼ばれる事になります。
returnで返す箇所を、以下のように変えてみましょう。

 ctx.OK([]byte("Hello World!\n"))

git diffで差分を見てみると、以下の差分が表示されます。

-     return nil
+     return ctx.OK([]byte("Hello World!\n"))
それでは、起動しているgoasampleをCtrl +Cで止めて、ビルド・起動して見ましょう。

$ go build
$ ./goasample

またcurlでアクセスして見ると

$ curl "http://localhost:8080"
Hello World!

Hello World!と表示されましたね!成功です!

感想

如何でしたでしょうか。
今回のサンプルは非常に小さなアプリケーションですが、
非常に簡単だったのではないでしょうか。 
ビジネスロジック以外の処理は、デザインした通りに勝手に生成してくれます。
次章では、自動生成されるファイルの説明をしていきます。

コメント

このブログの人気の投稿

自動生成されるファイル

自動生成されるファイル 先ほどのサンプルアプリケーションの作業ディレクトリには、
以下のファイル・ディレクトリがあります。
design/ app/ client/ swagger/ tool/ helloworld.go main.go
一つ一つ見ていきましょう。
生成物一覧
design/design.go
これは、最初に実装したデザインファイルになります。
アプリケーションの枠組みを決める設計図のようなものです。

goa独自のDSL(domain-specific language)を使って、APIの仕様を定義していきます。

app/
ここには、デザインされたアプリケーションの、実際に動くコードが生成されていきます。
基本的に、このディレクトリに格納されたファイルは編集不可(できるけど、しちゃだめ)と考えて良いです。

プログラマが追加実装しても、次にまたコード生成した場合、プログラマの追加実装は上書きされて消されてしまいます。

app/contexts.go
コンテキストが実装されています。
goaでは基本的に、API一つ一つに対して対応するコンテキストが実装されていきます。

コンテキストとは、HTTPリクエストを受け付けてからレスポンスを返すまで、
一連の流れが終わるまでの状態を表します。

2つのHTTPリクエストがあった場合、コンテキストは別々のオブジェクトとなります。
どのHTTPリクエストなのかを識別できるような、リクエスト毎の固有情報ですね。

コンテキストは3つのプロパティを持ち、
typeShowHelloworldContext struct { context.Context *goa.ResponseData *goa.RequestData}
Go標準のContextと、リクエスト・レスポンスを持っています。
app/controllers.go
コントローラのinterfaceが定義されています。
コントローラとは、Webアプリケーションでよく使われるデザインパターンMVC(Model View Controller)のControllerです。

コントローラはResource単位で生成され、今回のサンプルで言うと
HelloworldControllerというinterfaceが定義されています。

app/hrefs.go

goa -Golang Web Application Framework-

Webアプリケーションフレームワーク goa
goaとは、GolangでWebアプリケーションを構築していくフレームワークです。

今流行りのマイクロサービスやAPIサーバを早急に構築できます。(HTMLを返すWebサーバももちろん、実装可能です)

早急といった言葉を使ったのは、Webアプリケーションを構築する際の

ルーティングリクエストパラメータの定義レスポンスパラメータの定義パラメータのバリデーションコントローラの定義
といった、ビジネスロジック以外の土台部分を自動で生成してくれて、
ビジネスロジックの実装に専念できるからです。

自動生成といっても、思い描いたものが勝手にできる訳ではなく、
goa独自のDSLを記述してアプリケーションの仕様を定義する(デザインする)必要があります。

goaではアプリケーションの枠組みを、design.go(デザインファイル)というAPIの定義書を作って生成していきます。

DSLを覚える学習コストはありますが、それに見合った以上の働きをしてくれます。

Webアプリケーションのフレームワークとして有名なRuby on Railsも、
お作法を知らないと、レールから外れた実装をする事になり複雑な実装になってしまいます。

が、Railsは世界中で使われてる有名なフレームワークとなっています。
その学習コスト以上に見合った以上の働きがあるからでしょう。

goaは、ビジネスロジックに没頭できる点において、Railsに匹敵できるほどポテンシャルを持っていると思います。
もちろん、RubyではなくGo言語なので、言語仕様上の違いがあり単純に比較はできません。

このブログでは、自分の学習結果のアウトプットとして、goaについての紹介を連載していきたいと思います。

goa request parameters

Request この章では、APIリクエストのデザインについて説明していきます。
リクエストには、主に3種類、データの渡し方があります。

URLのPathに含まれる、リソースを識別するものとしてのパラメータQueryパラメータ(GETで良く使われる、?に続く文字列ですね)リクエストボディ
他にもHTTPリクエストという点では、HTTPヘッダも存在しますが、
HTTPヘッダはリクエストパラメータというより、
リクエストの形式や認証などに使われる、メタデータ的な意味合いがあるため
この章では扱いません。

それでは、それぞれの詳細を見ていきましょう。
Pathパラメータ RESTFulなAPIを扱うとき、良く出てくる

/user/:id -> /user/100
/product/:category/:product_id -> /product/book/300
のような、URLに含まれるパラメータですね。
正式名称は何というのでしょうか。

このパラメータは、Resourceを識別するために使われます。
Queryパラメータで識別しても良いのですが、Queryパラメータはどちらかというと、
APIの振る舞い・挙動を変える意味合いがあります。

この辺の考えは、RESTfulというアーキテクチャの話になってくるため、ここでは割愛します。

それでは実装してみましょう。
先ほどまでのソースコードを一旦、コミットします。
$ git add . $ git commit -m "first commit"
そして、デザインファイルにResourceを追加します。
var _ = Resource("products", func() { Action("show", func() { Routing(GET("products/:category_id/:product_id")) Params(func() { Param("category_id", Integer, "カテゴリID") Param("product_id", Integer, "プロダクトID") …