0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

7.12 関数型とインタフェース に関する考察 その1

Posted at

以下の記載について考察してみる

HTTPハンドラはHTTPサーバのリクエストを処理しますが、インタフェースによって定義されています。
type Handler interface {
    ServerHTTP(http.ResponseWriter, *http.Request)
}
どんな関数でもfunc(http.ResponseWriter, *http.Request)というシグネチャを持っていれば、
http.HandlerFuncに型変換することで、http.Handlerとして使用可能になります`
type HandlerFunc func(http.ResponseWriter, *http.Request)

func (f HandlerFunc) ServerHTTP(w http.ResponseWriter, r *http.Request) {
    f(w, r)

これをもうちょっと具体的なサンプルで説明すると以下のようになる

package main

import (
	"log"
	"net/http"
)

// ただの関数。シグネチャが (http.ResponseWriter, *http.Request)
func hello(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("hello!\n"))
}

func main() {
	mux := http.NewServeMux()

	// 1) Handler を要求する場所に「関数を型変換して」渡す
	mux.Handle("/hello", http.HandlerFunc(hello))

	// 2) いったん変数に入れてもOK(Handler として使える)
	var h http.Handler = http.HandlerFunc(hello)
	mux.Handle("/hello2", h)

	// 3) HandleFunc は「関数を直接受け取る」ヘルパ(内部で上の型変換をしてくれる)
	mux.HandleFunc("/hello3", hello)

	log.Fatal(http.ListenAndServe(":8080", mux))
}

http.HandlerFunc(hello)が型変換であることに注意。

もう少し実用的なサンプルとしては以下のようなものがある。


package main

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

// アプリ側の関数型
type AppHandler func(http.ResponseWriter, *http.Request)

// Handler を満たす(これで AppHandler をどこでも http.Handler として使える)
func (h AppHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { h(w, r) }

// ミドルウェアを“メソッド”として重ねがけできる
func (h AppHandler) WithLogging(l *log.Logger) AppHandler {
	return func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()
		h(w, r)
		l.Printf("%s %s %v", r.Method, r.URL.Path, time.Since(start))
	}
}

func (h AppHandler) WithRecover(l *log.Logger) AppHandler {
	return func(w http.ResponseWriter, r *http.Request) {
		defer func() {
			if rec := recover(); rec != nil {
				l.Printf("panic: %v", rec)
				http.Error(w, "internal error", http.StatusInternalServerError)
			}
		}()
		h(w, r)
	}
}

func hello(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("hello!\n"))
}

func main() {
	mux := http.NewServeMux()

	// 関数 → AppHandler に変換 → WithRecover, WithLoggingメソッドを呼んでデコレート → Handler として登録
	h := AppHandler(hello).
		WithRecover(log.Default()).
		WithLogging(log.Default())

	mux.Handle("/hello", h) // h は http.Handler

	log.Fatal(http.ListenAndServe(":8080", mux))
}

上述のようにWithRecover, WithLoggingを重ねた場合、各メソッド中のh(w, r) の実態は hello(w, r) となる。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?