Negroni + Gorilla/Muxで簡単なサーバーアプリケーション作る
使用するライブラリは以下のとおり
サンプルコード
作った機能としては、
- GET "/" で名前の書き込みを依頼し
- POST "/"で送信された名前を受け取り表示させる。
- GET "/admin" でちょっとした認証機構
package main
import (
"fmt"
"net/http"
"html/template"
"github.com/gorilla/mux"
"github.com/codegangsta/negroni"
)
func main(){
r := mux.NewRouter()
// Root への処理
r.HandleFunc("/", getRoot).Methods("GET")
r.HandleFunc("/", postRoot).Methods("POST")
// アドミン周りの処理
authBase := mux.NewRouter()
r.PathPrefix("/admin").Handler(negroni.New(
negroni.NewRecovery(),
negroni.NewLogger(),
negroni.HandlerFunc(authenticate),
negroni.Wrap(authBase)))
auth := authBase.PathPrefix("/admin").Subrouter()
auth.Path("/").HandlerFunc(getAdmin)
n := negroni.Classic()
n.UseHandler(r)
n.Run(":3000")
}
func getRoot(w http.ResponseWriter, r *http.Request) {
t := template.Must(template.ParseFiles("root.tmpl"))
t.Execute(w, nil)
}
func postRoot(w http.ResponseWriter, r *http.Request) {
name := r.FormValue("name")
t := template.Must(template.ParseFiles("root.tmpl"))
t.Execute(w, struct{Name string} {Name : name})
}
func getAdmin(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Authenticate Success!!")
}
func authenticate(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
// ダメダメな実装例なので、真似ダメゼッタイ
name := r.FormValue("name")
if(name == "auth"){
next(w,r)
} else {
fmt.Fprintf(w,"Please Authenticate!!")
}
}
Negroni についての解説
Negroni の構造について
Negroni コード内に存在する重要な定義は、ほんの少ししか存在しません。
// Negroni is a stack of Middleware Handlers that can be invoked as an http.Handler.
// Negroni middleware is evaluated in the order that they are added to the stack using
// the Use and UseHandler methods.
type Negroni struct {
middleware middleware
handlers []Handler
}
func (n *Negroni) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
n.middleware.ServeHTTP(NewResponseWriter(rw), r)
}
まず、negroni.Classci()
もしくは、negroni.New()
を実行すると返されるNegroniインスタンスにフィールドは二つしか無く、ハンドラの配列とミドルウェアだけです。
ハンドラって何やねん。
ハンドラとは、「ユーザーからの要求(GET /posts/ みたいなリクエスト)をどう処理して応答するか」を操作する何かの事。
まず、Golang には標準でnet/http
パッケージが用意されており、コレを使うだけで、以下のようにリクエストをハンドリングする事が出来ます。
http.Handle("/foo", fooHandler)
http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
})
log.Fatal(http.ListenAndServe(":8080", nil))
このことを踏まえた上で、Negroni はこのhttp/net
と互換性を持たせるように設計されており、http/net
のハンドラを、Negroni のハンドラとして利用することが出来るようになっています。それを実現しているのがNegroni.go の以下の部分。
type Handler interface {
ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
}
// HandlerFunc is an adapter to allow the use of ordinary functions as Negroni handlers.
// If f is a function with the appropriate signature, HandlerFunc(f) is a Handler object that calls f.
type HandlerFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
func (h HandlerFunc) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
h(rw, r, next)
}
ミドルウェアって何やねん!?
この概念を理解するのに結構時間がかかりました。参考文献にも同じ図が載っていますが、この図が全てだと思います。
https://mattstauffer.co/blog/laravel-5.0-middleware-filter-style#what-is-middleware
コアロジックであるApp に対して、前処理と後処理を提供するものが、ミドルウェアです。セッション管理や認証などがそれに当たります。ではコードを見てみましょう。
type middleware struct {
handler Handler
next *middleware
}
middleware の定義はたったこれだけで、実行するハンドラと、次に実行するミドルウェアへのポインタが定義されています。negroni.Classic
を呼び出すと、外側から順に、Recovery、Logging、Staticのミドルウェアが自動的に登録され、negroni.New
を呼び出すと、ミドルウェアが一つもない状態でNegroniインスタンスが作成されます。
また、n.Use
を用いると、自作のミドルウェアを登録することが出来ます。(セッション管理とか認証系とか自作するか別ライブラリを使う必要がある) useを使ってミドルウェアを登録する時、たまねぎ構造の最も内側にミドルウェアが登録されます。
公式README曰く、以下の形で定義してねって書いてあるので、これに準拠しましょう。
func MyMiddleware(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
// do some stuff before
next(rw, r)
// do some stuff after
}
使ってみた雑感
Martini のREADME.md に**The martini framework is no longer maintained.**と記述されているので、これからはNegroni/GorilalMux の組み合わせが主流になるのかもしれない。
けど、色々と面倒くさいところが多いし、開発速度を重視する人には向いていないかも……。
ここもっと良い書き方出来るとかココ間違ってる等のご指摘がありましたら、いただけると幸いです。