ミドルウェア(高度)

やあ、フィリップです!
私のプラットフォーム Go Web Examples Courses がローンチしたことをお知らせします。GoでのWeb開発に関する分かりやすいビデオコースをお楽しみください。初期サポーター向けの特別オファーをぜひチェックしてください。
そこで会いましょう!:)
詳細はこちら

ミドルウェア(高度)

この例では、Goでより高度なバージョンのミドルウェアを作成する方法を示します。

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

ここでは、複数のミドルウェアを連鎖させやすくするために、新しい型Middlewareを定義します。このアイデアは、Mat Ryer氏のAPI構築に関する講演に触発されました。講演を含む詳細な説明はこちらにあります。

このスニペットは、新しいミドルウェアがどのように作成されるかを詳細に説明しています。以下の完全な例では、いくつかのボイラープレートコードを削減しています。

func createNewMiddleware() Middleware {

    // Create a new Middleware
    middleware := func(next http.HandlerFunc) http.HandlerFunc {

        // Define the http.HandlerFunc which is called by the server eventually
        handler := func(w http.ResponseWriter, r *http.Request) {

            // ... do middleware things

            // Call the next middleware/handler in chain
            next(w, r)
        }

        // Return newly created handler
        return handler
    }

    // Return newly created middleware
    return middleware
}

これが完全な例です。
// advanced-middleware.go
package main

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

type Middleware func(http.HandlerFunc) http.HandlerFunc

// Logging logs all requests with its path and the time it took to process
func Logging() Middleware {

    // Create a new Middleware
    return func(f http.HandlerFunc) http.HandlerFunc {

        // Define the http.HandlerFunc
        return func(w http.ResponseWriter, r *http.Request) {

            // Do middleware things
            start := time.Now()
            defer func() { log.Println(r.URL.Path, time.Since(start)) }()

            // Call the next middleware/handler in chain
            f(w, r)
        }
    }
}

// Method ensures that url can only be requested with a specific method, else returns a 400 Bad Request
func Method(m string) Middleware {

    // Create a new Middleware
    return func(f http.HandlerFunc) http.HandlerFunc {

        // Define the http.HandlerFunc
        return func(w http.ResponseWriter, r *http.Request) {

            // Do middleware things
            if r.Method != m {
                http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
                return
            }

            // Call the next middleware/handler in chain
            f(w, r)
        }
    }
}

// Chain applies middlewares to a http.HandlerFunc
func Chain(f http.HandlerFunc, middlewares ...Middleware) http.HandlerFunc {
    for _, m := range middlewares {
        f = m(f)
    }
    return f
}

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

func main() {
    http.HandleFunc("/", Chain(Hello, Method("GET"), Logging()))
    http.ListenAndServe(":8080", nil)
}
$ go run advanced-middleware.go
2017/02/11 00:34:53 / 0s

$ curl -s http://localhost:8080/
hello world

$ curl -s -XPOST http://localhost:8080/
Bad Request