golang 中间件如js闭包,亦似python中装饰器。本质走的还是偏函数,柯里化,只不过执行改造成管道式。

Basic

http
http.HandlerServeHTTPhttp.HandlerFuncHTTP handlershttp.HandleDefaultServeMuxhttp.Handler
http.HandlerFunchttp.HandlerFunc
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)
}

测试

httpie
$ 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

$ http :8080/foo
$ http :8080/bar
$ http :8080/foo?bar

Advance

package main

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

// 定义中间件类型
type Middleware func(http.HandlerFunc) http.HandlerFunc

func Logging() Middleware {
    // 创建中间件
    return func(f http.HandlerFunc) http.HandlerFunc {
        // 定义 http.HandlerFunc
        return func(w http.ResponseWriter, r *http.Request) {
            start := time.Now()
            defer func() {
                log.Println(r.URL.Path, time.Since(start))
            }()

            // 在链中调用下一个中间件或handler
            f(w, r)
        }
    }
}

// 谓词中间件,确保请求方式是指定的http谓词动作
func Method(m string) Middleware {
    return func(f http.HandlerFunc) http.HandlerFunc {
        return func(w http.ResponseWriter, r *http.Request) {
            if r.Method != m {
                http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
                return
            }

            f(w, r)
        }
    }
}

// 调用链串应用多个中间件产生一个 http.HandlerFunc
// 类似pipe 管道, 上游的输出视为下游的输入
func Chain(f http.HandlerFunc, middlewares ...Middleware) http.HandlerFunc {
    for _, m := range middlewares {
        f = m(f)
    }
    return f
}

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

func main() {
    http.HandleFunc("/", Chain(Hi, Method("GET"), Logging()))
    http.ListenAndServe(":8080", nil)
}

测试

发起请求

C:\Users\Administrator>http post :8080/hi
HTTP/1.1 400 Bad Request
Content-Length: 12
Content-Type: text/plain; charset=utf-8
Date: Fri, 19 Mar 2021 11:17:13 GMT
X-Content-Type-Options: nosniff

Bad Request


C:\Users\Administrator>http :8080/hi
HTTP/1.1 200 OK
Content-Length: 14
Content-Type: text/plain; charset=utf-8
Date: Fri, 19 Mar 2021 11:17:33 GMT

Hi, pardon110

服务端响应

D:\code-base\gomod\tt\web>go run "d:\code-base\gomod\tt\web\middleware\advance\advancet.go"
2021/03/19 19:17:13 /hi 0s
2021/03/19 19:17:33 /hi 0s
本作品采用《CC 协议》,转载必须注明作者和本文链接