元営業WEBエンジニアのアプリ開発日記

営業出身のWEB系エンジニアが気になったものから作ってはメモを残してくブログ

Goでルーティング(gorilla/mux)

概要

GAEのgoランタイムでDatastoreに繋いでCRUDしてみたい。
その前にgo言語自体全然書いてないのでルーティングをザクっと実装してみる。
その後でDatastoreへの接続を書いていく。ちょっとずつ進めてこう。

構築

リクエストのルーティング処理

ハンドラーの登録

とりあえず、GET,POST,PUT,DELETEあたりをやりたい
前回のHello, Worldではnet/httpでリクエストをさばいてたけど、gorilla/muxなるものが人気らしい。 それで書いてみる。

ListenAndServeメソッドの第2引数にルーティング情報を記載したハンドラーを渡してみる。

func main() {
    port := os.Getenv("PORT")
    if port == "" {
        port = "8080"
        log.Printf("Defaulting to port %s", port)
    }
    log.Printf("Listening on port %s", port)
    log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), registerHandlers()))
}

ルーティングの定義

メソッド、パスに準じた処理を記載。

func registerHandlers() *mux.Router {
    router := mux.NewRouter()
    router.Methods("GET").Path("/members").Handler(appHandler(listHandler))
    router.Methods("POST").Path("/members").Handler(appHandler(createHandler))
    router.Methods("PUT").Path("/members/{id:[0-9]+}").Handler(appHandler(updateHandler))
    router.Methods("DELETE").Path("/members/{id:[0-9]+}").Handler(appHandler(deleteHandler))
    return router
}

エラー返却時の記述を簡略化

Error handling and Go - The Go Blogに書かれているように自身の定義したエラー(appError)を返却するappHandlerをかましてハンドラー登録すると、http.ResponseWriterに毎度エラーを書き込まなくても、エラーを返却するだけでよろしくエラー返却してくれて冗長性を無くしてくれるらしい!

type appError struct {
    Code    int
    Message string
    Error   error
}

type appHandler func(w http.ResponseWriter, r *http.Request) *appError

ただし、ただエラー返却するだけではnet/httpが理解してくれないので
ServeHTTPメソッドを作成して理解させる必要があるらしい。  

func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    if e := fn(w, r); e != nil {
        log.Printf("Handler error: status code: %d, message: %s, underlying err: %#v",
            e.Code, e.Message, e.Error)
        http.Error(w, e.Message, e.Code)
    }
}

ルーティング毎の処理(一覧表示)

あとはルーティングにマッチした際の処理を記載する。
本当はDatastoreからデータ取りたいが、とりあえず適当に会員一覧を返却させとく。

type Member struct {
    Name string
}

type Members []Member

func listHandler(w http.ResponseWriter, r *http.Request) *appError {
    response, jsonError := json.Marshal(Members{
        Member{
            Name: "Name1",
        },
        Member{
            Name: "Name2",
        },
    })
    if jsonError != nil {
        return appErrorFormat(jsonError, "%s", jsonError)
    }

    w.Header().Set("Content-Type", "application/json")
    _, writeError := w.Write(response)
    if writeError != nil {
        return appErrorFormat(writeError, "%s", writeError)
    }
    return nil
}

実行

あとはコマンド叩いてローカル環境で色々アクセスしてみる。

# go run main.go
go build
./ucwork-go

GETでhttp://localhost:8080/membersにアクセスしたらこんなん返ってきた!!

[
    {
        "Name": "Name1"
    },
    {
        "Name": "Name2"
    }
]

まとめ

GCPのサービスザクっと触ってみるのが目的なのにまた脱線してしまった。。。
まぁせっかくならGoogleにロックインされにかかるためにgoも学習しとこー