Go
golang
Web
go-web-examples

Go Web Examplesの和訳(Middleware: Basic)

Go勉強会 Webアプリケーション編 #3 でやろうと思っている Go Web Examples: Middleware (Basic)の和訳です。と言ってもほとんど解説文がないので訳するところもないのですけどねー


Middleware: Basic

この例では Go で簡単なロギングのミドルウェアを作る方法を示します。

ミドルウェアは単に http.HandlerFunc を引数の一つとして受け取り、それをラップし、サーバーが呼び出すための新しい httpHandlerFunc を返します。

// basic-middleware.go

package main

import (
"fmt"
"log"
"net/http"
)

func logging(f http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
log.Println(r.URL.Path)
f(w, r)
}
}

func foo(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "foo")
}

func bar(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "bar")
}

func main() {
http.HandleFunc("/foo", logging(foo))
http.HandleFunc("/bar", logging(bar))

http.ListenAndServe(":8080", nil)
}

$ go run basic-middleware.go

2017/02/10 23:59:34 /foo
2017/02/10 23:59:35 /bar
2017/02/10 23:59:36 /foo?bar

$ curl -s http://localhost:8080/foo
$ curl -s http://localhost:8080/bar
$ curl -s http://localhost:8080/foo?bar


補足

駆け出し Gopher (私もですが)がこれを初見で理解するのはちょっときついと思います。

ミドルウェアとはなんでしょうか? Wikipedia によると


コンピュータの基本的な制御を行うオペレーティングシステム(OS)と、各業務処理を行うアプリケーションソフトウェアとの中間に入るソフトウェアのこと。


とありますが、ここで言っているのはそんなたいそうなもののことではなく、HTTP ハンドラコードの前後に差し込んでなんかちょっとやらせるためのコードのことです。

この例のコードでミドルウェアを使う前のコードは次のようになっていたはずです。

package main

import (
"fmt"
"net/http"
)

func foo(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "foo")
}

func bar(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "bar")
}

func main() {
http.HandleFunc("/foo", foo)
http.HandleFunc("/bar", bar)

http.ListenAndServe(":8080", nil)
}

単純に foo/bar をレスポンスとして返すだけのものです。

そこに登場するのが次のミドルウェアです。

func logging(f http.HandlerFunc) http.HandlerFunc {

return func(w http.ResponseWriter, r *http.Request) {
log.Println(r.URL.Path)
f(w, r)
}
}

この http.HandlerFunc を受け取って新しい http.HanlderFunc を返す logging という関数がミドルウェアです。この関数はログを一行出力してから元々の http.HandlerFuncf を実行する関数を新しく作って返すものです。

この logging ともともとの foo を組み合わせて

http.HandleFunc("/foo", foo)

となっていたところを

http.HandleFunc("/foo", logging(foo))

を書き換えてやるとログを一行出力してから foo を呼び出す関数を作って http.HandleFunc に渡してやることができ、クライアントからリクエストがあるたびにログを出力して foo というレスポンスを返すことができます。

このようにしてログの出力処理を差し込むということをやっています。