読者です 読者をやめる 読者になる 読者になる

goa tips : Attribute と Param と Member は同じもの

はじめに

API デザインの書き方を一通り説明したので,コントローラーの実装の説明する前に, goa の tips をいくつか取り上げたいと思います. という訳で,AttributeParamMember も,すべて Attributeエイリアス関数で同じものなんです.というお話.

使い分け

AttributeParamMember 同じものといっても,使う場面が切り分けられています. ちゃんと使うべきところで使うと読みやすいデザインになるようになっているはずです(たぶん).

使う場面 説明
Attribute MediaType定義の Attributes のなか レスポンスデータの要素の定義として
Param Resource定義の Action の Params のなか endpoint にアクセスする際のパスパラメータやフォームパラメータの要素の定義として
Member Type定義のなか Payloadやユーザー定義の新しい型の要素の定義として

でもまぁ,結局同じなんでどれで書いても目的は達成できます.

基本的な使い方

これらが同じものだと分かっていれば,使い方も覚えやすいです.

Param(<名前>, <型>, <説明>, func() {
        // バリデーション
        // デフォルト値の設定 (これは Payload のときのみ有効)
        // 使用例
})

具体的に書くとこんな感じ.

Param("bottleID", Integer, "Bottle ID", func() {
        Minimum(0)
        Maximum(127)
        Default(1)

        Example(3)
})

バリデーションが不要な場合はこう.

Param("bottleID", Integer, "Bottle ID")

説明が不要ならこう.

Param("bottleID", Integer)

実はもっと短く書けて,

Param("comment")

なんてのもできます.ただし,このとき,"comment" の型は String に指定されます.

バリデーション

前にも説明しましたけど,再掲しておきます.

バリデーション DSLの例 説明
最大/最小 Maximum(100) / Minimum(0) 数値 (Integer, Number) で利用できる
最大長/最小長 MaxLength(10) / MinLength(1) 文字列,配列で利用できる.文字列は rune 文字長でカウントされる
パターン Pattern(“^foo”) 文字列に対して正規表現を利用できる
列挙 Enum(3, 5, 7) / Emum(“male”, “female”) 数値や文字列に対して列挙したものだけを受け付けるようにできる
定型 Format(“date-time”) / Format(“email”) / Format(“ipv4”) / Format(“mac”) goa で定型フォームとして用意されているものがいくつかあります

型はどのようなものが指定可能か?

goa の基本型には以下のようなものがあります.

goaの基本型 golangでの表現 JSONでの表現
Integer int number
Number float number
String string string
Boolean bool boolean
DateTime time.Time RFC3339な文字列
UUID uuid.UUID RFC4122な文字列
Any interface{}

型に以下の関数を適用したものも型として利用できます.例えば,IntegerArrayOf を適用した ArrayOf(Integer)[1,2,3,4,5] のような JSON に対応します. また,Type 関数で定義したものも型として利用できます.というか,Type はユーザー定義の型を作る関数です. ややこしいですが,MediaType で定義したものも型として利用できます.ただし,Type 関数で定義する新しい型のなかに MediaType を入れてはいけません. Type で定義した型を利用して MediaType を構築するのは ok です.

関数 説明
ArrayOf(…) ArrayOf(Integer), ArrayOf(Bottle) 配列表現にした Type を返す.ex. [1, 2, 3, 4]
HashOf(…) HashOf(String, Integer) ハッシュマップ.JSON オブジェクトに相当.ex. {"value" : 123}
Type(…) 参照:Type定義 Type関数で新しく定義された型
MediaType(…) 参照:MediaType定義 MediaType関数で定義されたもの.MediaType も Type の特殊なものとして利用できる.ただし制限がある(上記参照)
CollectionOf(…) ArrayOf(BottleMedia) 配列表現にした MediaType を返す

Default について

Default はパラメータにデフォルト値を設定できますが,これは,Payload のパラメータにしか効きません. Make it possible to provide default values for query string and header params · Issue #658 · goadesign/goa · GitHub

改良されてフォームパラメータに対しても有効になりました 🙌 .

Assign to default param values. by brycereitano · Pull Request #1024 · goadesign/goa · GitHub

Example の書き方

Example は基本型に対しては値を書くだけで ok です.Example を設定しなくても,ドキュメントにはランダムな Example が設定されます. 大変親切なのですが,これが割とジャマです(^^ゞ.なので,なるべく Example で指定するか,明示的に NoExample 関数を指定してサンプルを作らないようにするかした方がいいです(個人の趣味的には).

例:レスポンスの MediaType の Example がランダムに作られた例 (図は swagger-ui で見た場合)

f:id:ikawaha:20160915181740p:plain

Type で組み上げた型について Example を適用するには,golang での型を組み合わせて値をセットしていきます. たとえば,HashOf(String, Integer) でワインの銘柄と年を Payload として与えるとします. HashOf(String, Integer)に対しては golangmap[string]int が対応するので,この型の例としては以下のように map で与えてやります.

var BottlePayload = Type("BottlePayload", func() {
        Member("bottles", HashOf(String, Integer), func() {
                Example(map[string]int{
                        "POUILLY FUISSE": 2014,
                })
        })
})

swagger-ui で見たとき(下図)で,Payload のサンプルにワインの銘柄(“POUILLY FUISSE”)と年(2014) がセットされているのが分かると思います.

f:id:ikawaha:20160915182447p:plain

このように,swagger-ui などを通じて API を叩いたりするときに分かりやすいので,Example は適切にセットしておくことをオススメします.