GoでRESTfulなWebアプリを作りたい (go-restfulを触ってみる)

## 概要
GoでAPIサーバーを書こうと思ったらどうなるのかなーっと思い立ったのでやってみた。
APIの受け答えはJSON形式で行い、加えてダッシュボードみたいなUIも提供できるようなものを想定している。
DBとかデザインとかはそれぞれ別のレイヤーになるので今回はリクエストとレスポンスまで。

## 準備
今回使うのは `go-restful`というパッケージ。RESTfulなHTTPサーバーを作るのに使う様子。
https://github.com/emicklei/go-restful

予め `go get` しておこう。

```bash
go get github.com/emicklei/go-restful
```

似たもので `go-json-rest` というものもあったが、JSONでの受け答え専用のようだったので今回はパスした。

## コード全文
コードを説明して回るのも大変長くなるのでザッと書き出しておく :ghost:

```go
package main

import (
	"github.com/emicklei/go-restful"
	"io"
	"net/http"
)

// Helloに渡す値を格納する
type resHello struct {
	Name string
	Memo string `json:",omitempty"`
}

// エラーレスポンス用の構造体
type resError struct {
	Error string
}

// "GET /hello" を処理する関数
func getHello(req *restful.Request, resp *restful.Response) {
	// GETのときは text/plain で文字列を返す
	io.WriteString(resp, "please tell me your name to /hello by POST method :)")
}

// "POST /hello" を処理する関数
func postHello(req *restful.Request, resp *restful.Response) {
	// リクエストを resHello に割り当てる
	param := new(resHello)
	req.ReadEntity(&param)

	// 適当なエラーハンドリング
	if param.Name == "" {
		// エラーの内容をJSONで返す
		resp.WriteHeaderAndJson(http.StatusInternalServerError, &resError{"Name is required"}, restful.MIME_JSON)
		return
	}

	// 結果を出力する (自動で application/json が指定される)
	resp.WriteAsJson(&resHello{Name: param.Name})
}

func main() {
	// restfulを初期化してルーティングの設定を行う
	ws := new(restful.WebService)
	ws.Route(ws.GET("/hello").To(getHello))
	ws.Route(ws.POST("/hello").To(postHello))
	restful.Add(ws)

	// 8080ポートで待ち受ける
	http.ListenAndServe(":8080", nil)
}
```

## 実行結果
上記のコードは `go run` でサーバーを立ち上げられる。
立ち上げたサーバーにはブラウザアクセスか、cURLでアクセスできる。

### GET /hello

ブラウザやcURLで http://localhost:8080/hello にアクセスすると下記のような結果が得られる。

```bash
$ curl -i http://127.0.0.1:8080/hello
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    52  100    52    0     0   3250      0 --:--:-- --:--:-- --:--:--  3250HTTP/1.1 200 OK
Date: Sat, 02 Jan 2016 13:20:14 GMT
Content-Length: 52
Content-Type: text/plain; charset=utf-8

please tell me your name to /hello by POST method :)
```

ちなみにあえて指定を入れていない `/` は `404 Page Not Found` となる

```bash
$ curl -i http://127.0.0.1:8080/
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    19  100    19    0     0     19      0  0:00:01 --:--:--  0:00:01 19000HTTP/1.1 404 Not Found
Date: Sat, 02 Jan 2016 13:20:42 GMT
Content-Length: 19
Content-Type: text/plain; charset=utf-8

404: Page Not Found

```

### POST /hello

cURLを使ってPOSTを叩くと下記のような応答になる。`WriteAsJson` で Content-type はよしなにやってくれる。
`resHello` で `Memo` に指定した `omitempty` (空の時は切り捨て) は有効な様子。

```bash
$ curl -i -H 'Content-Type: application/json' -d '{"Name":"Gopher"}' http://127.0.0.1:8080/hello
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    40  100    23  100    17   1437   1062 --:--:-- --:--:-- --:--:--  1437HTTP/1.1 200 OK
Content-Type: application/json
Date: Sat, 02 Jan 2016 13:11:48 GMT
Content-Length: 23

{
  "Name": "Gopher"
 }
```


ちなみにエラーハンドリングしている箇所はこのような応答になる。
JSONであることがわかっているのに `WriteHeaderAndJson` に `restful.MIME_JSON` を指定してあげないといけないのは
ちょっと微妙かもと思ったが、もしかしたら使いどころが別にあるのかもしれない。

```bash
$ curl -i -H 'Content-Type: application/json' -d '{"Name":""}' http://127.0.0.1:8080/hello
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    45  100    34  100    11   2125    687 --:--:-- --:--:-- --:--:--  2125HTTP/1.1 500 Internal Server Error
Content-Type: application/json
Date: Sat, 02 Jan 2016 13:09:46 GMT
Content-Length: 34

{
  "Error": "Name is required"
 }
```

## 感想

- GoでHTTPサーバーと一体なアプリは意外とサクッと作れた。
- サンプルコードも多めでGoDoc対応にも対応していたので割と安心。
    - https://godoc.org/github.com/emicklei/go-restful
    - https://github.com/emicklei/go-restful/tree/master/examples
- go-restful はXMLの受け答えにも対応している様子。
- WebアプリってPHP(CakePHPやLaravel)とかRuby(Rails、Sinatra)とかで作るイメージが強いけど、  
  Goで実運用したりして問題になったりしないかは未知数。`net/http`のベンチマークとかあるのかな？