golang 中介軟體

pardon110 發表於 2021-03-19

golang 中介軟體如js閉包,亦似python中裝飾器。本質走的還是偏函式,柯里化,只不過執行改造成管道式。

Basic

http幾個相關的概念

  • http.Handler 只有一個ServeHTTP操作的介面
  • http.HandlerFunc 適配普通函式為 HTTP handlers
  • http.Handle 允許用正則字串在 DefaultServeMux 上註冊 http.Handler

示例 日誌中介軟體 將 http.HandlerFunc作為入參,作了包裝並返回新的http.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 協議》,轉載必須註明作者和本文連結