Go製WAFのuconを触ってみた

## ucon

仕事で使うことになったので触ってみました。
とりあえず基本機能だけです。
https://github.com/favclip/ucon

## まとめ

* handlerの引数は`Bubble.Arguments`に設定されているものが使用される
* handlerの型が`interface{}`なので気をつけよう
* reflectの知識が前提なのでreflect好きな人向け
* 私みたいな一般人は大人しく`Gin`,`Echo`,`Beego`あたりを使うのがいいかも

## 基本

```go:hello.go
package main

import (
	"net/http"

	"github.com/favclip/ucon"
)

func init() {
	// register middleware
	ucon.Middleware(ucon.HTTPRWDI())
	ucon.HandleFunc("GET", "/hello", getHello)

	http.Handle("/", ucon.DefaultMux) // appengine向け
    // ucon.ListenAndServe(":8080") 普通のサーバ向け
}

func getHello(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Hello"))
}
```

**Point**:
`ucon.Middleware(ucon.HTTPRWDI())`を忘れるとエラーとなります。
なぜなら、`ucon.HTTPRWDI()`を実行することでhandlerに`w http.ResponseWriter, r *http.Request`が渡されるため。

## context.Contextを使う

```go:use_context.go
package main

import (
	"net/http"

	"github.com/favclip/ucon"
    "golang.org/x/net/context"
)

func init() {
	// register middleware
	ucon.Middleware(ucon.HTTPRWDI())
    ucon.Middleware(ucon.NetContextDI()) // 追加
	ucon.HandleFunc("GET", "/hello", getHello)

	http.Handle("/", ucon.DefaultMux)
    // ucon.ListenAndServe(":8080")
}

func getHello(w http.ResponseWriter, r *http.Request, ctx context.Context) {
    w.Write([]byte("Hello"))
}
```

**Point**:
上記の通り、`ucon.Middleware(ucon.NetContextDI())`を追加する。理由は先ほどと同様。
そうすればhandler内で`ctx context.Context`などとして使用できる。

## routing parameterを取得する

```go:get_routing_parameter.go
package main

import (
	"net/http"

	"github.com/favclip/ucon"
)

func init() {
	// register middleware
	ucon.Middleware(ucon.HTTPRWDI())
    ucon.Middleware(ucon.NetContextDI())
	ucon.HandleFunc("GET", "/hello/{id}", getHello)

	http.Handle("/", ucon.DefaultMux)
    // ucon.ListenAndServe(":8080")
}

func getHello(w http.ResponseWriter, r *http.Request, ctx context.Context) {
    params := ctx.Value(ucon.PathParameterKey).(map[string]string)
    id := params["id"]
    w.Write([]byte(fmt.Sprintf("Hello, No.%s.", id)))
}
```

**Point**:
routing parameterの指定は`{id}`のように`{}`でくくらなければなりません。（内部で正規表現を使用しているため）
取得は、まず`ctx.Value(ucon.PathParameterKey).(map[string]string)`でparameter全てを取ってきます。
そこから`{}`でくくった文字列をmapのkeyに指定します。

## ucon.RequestObjectMapper()

requestのbodyを自動的にstructに変換するmiddlewareのようです。

```go:use_requestObjectMapper.go
package main

import (
	"net/http"

	"github.com/favclip/ucon"
)

type Hello struct {
    msg  string `json:"msg"`
    name string `json:"name"` 
}

func init() {
	// register middleware
	ucon.Middleware(ucon.HTTPRWDI())
    ucon.Middleware(ucon.NetContextDI())
	ucon.Middleware(ucon.RequestObjectMapper()) // 追加
	ucon.HandleFunc("POST", "/hello/new", getHello)

	http.Handle("/", ucon.DefaultMux)
    // ucon.ListenAndServe(":8080")
}

func postHello(w http.ResponseWriter, r *http.Request, ctx context.Context, hello *Hello) {
    err := json.NewEncoder(w).Encode(hello)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
}
```

**Point**:
`ucon.Middleware(ucon.RequestObjectMapper())`を忘れるとエラーとなります。
parseしたいstructを引数に指定するだけでOK。なお、`Hello`ではなく`*Hello`とPoninterを指定する必要があります。

## ucon.ResponseMapper()

responseのbodyを自動的にstructからjsonに変換するmiddlewareのようです。

```go:use_responseMapper.go
package main

import (
	"net/http"

	"github.com/favclip/ucon"
)

type Hello struct {
    msg  string `json:"msg"`
    name string `json:"name"` 
}

func init() {
	// register middleware
	ucon.Middleware(ucon.HTTPRWDI())
    ucon.Middleware(ucon.NetContextDI())
	ucon.Middleware(ucon.RequestObjectMapper())
	ucon.Middleware(ucon.ResponseMapper()) // 追加
	ucon.HandleFunc("POST", "/hello/{id}", getHello)

	http.Handle("/", ucon.DefaultMux)
    // ucon.ListenAndServe(":8080")
}

func getHello(w http.ResponseWriter, r *http.Request, ctx context.Context) *Hello {
    return &Hello{
        msg:  "Hello",
        name: "rglay",
    }
}
```

**Point**:
`ucon.Middleware(ucon.ResponseMapper())`を忘れるとエラーとなります。
parseしたいstructをreturnするだけでOK。なお、`Hello`ではなく`*Hello`とPoninterを指定する必要があります。
`ucon.HandleFunc`はhandlerが`interface{}`であることを許容するため出来る技かな、と思いました。

## middleware

公式のREADMEにサンプルがあるのでそれをみてください。
handlerの引数で使う場合は、`Bubble.Arguments`に設定することを忘れずに！！
大事なことなので何度でも言います！！

## 次回

次回はswaggerのpluginを触ってみようと思います。