Skip to content

🐈 Nekonote.

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

January 01, 2016

概要

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での受け答え専用のようだったので今回はパスした。

コード全文

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

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 にアクセスすると下記のような結果が得られる。

$ 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 はよしなにやってくれる。 resHelloMemo に指定した 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であることがわかっているのに WriteHeaderAndJsonrestful.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のベンチマークとかあるのかな?

関連リンク


忘備録や調べたことなどを気が向いたときに書いたりします。