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

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

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...