概要
GoでAPIサーバーを書こうと思ったらどうなるのかなーっと思い立ったのでやってみた。
APIの受け答えはJSON形式で行い、加えてダッシュボードみたいなUIも提供できるようなものを想定している。
DBとかデザインとかはそれぞれ別のレイヤーになるので今回はリクエストとレスポンスまで。
準備
今回使うのは go-restful
というパッケージ。RESTfulなHTTPサーバーを作るのに使う様子。
https://github.com/emicklei/go-restful
予め go get
しておこう。
go get github.com/emicklei/go-restful
似たもので go-json-rest
というものもあったが、JSONでの受け答え専用のようだったので今回はパスした。
コード全文
コードを説明して回るのも大変長くなるのでザッと書き出しておく
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(¶m)
// 適当なエラーハンドリング
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 にアクセスすると下記のような結果が得られる。
$ 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
となる
$ 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
(空の時は切り捨て) は有効な様子。
$ 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
を指定してあげないといけないのは
ちょっと微妙かもと思ったが、もしかしたら使いどころが別にあるのかもしれない。
$ 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対応にも対応していたので割と安心。
- go-restful はXMLの受け答えにも対応している様子。
- WebアプリってPHP(CakePHPやLaravel)とかRuby(Rails、Sinatra)とかで作るイメージが強いけど、
Goで実運用したりして問題になったりしないかは未知数。net/http
のベンチマークとかあるのかな?