Go語言中介軟體(Middleware)
首先,什麼是Handler?
簡單來說,go Web通過http.HandleFunc()來註冊預設路由,將傳入URL匹配到相應的Handler。
它的函式原型為:
http.HandleFunc(pattern string, handler func(ResponseWriter, *Request))
其中,Handler是我們處理請求和生成返回資訊邏輯處理函式。
什麼是中介軟體呢?
中介軟體(MiddleWare)實際上就是一個返回值為Handler的中間處理函式。
中介軟體有啥用呢?
有時候在執行實際Handler裡面的邏輯的時候想要預處理或者後處理一些行為(比如寫入log、統計執行時間等等);有時候我們想要在呼叫一個Handler之前或之後呼叫另一個Handler。
舉個例子,就像泡咖啡,但是泡完咖啡還不好喝,我們還要往裡面加奶,這時聰明的人們就發明了速溶奶咖,這樣把泡咖啡和加奶的流程結合到了一起~泡咖啡就相當於我們原先的Handler,速溶奶咖就相當於中介軟體。
單中介軟體
下面這個例子是單個的中介軟體,這個中介軟體實現了我們的第一個需求:在執行Handler的邏輯之前/之後乾點別的事情。
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)
}
可以看到logging是一個返回型別為HandlerFunc的中介軟體,
ps.HandlerFunc的定義如下:
type HandlerFunc func(ResponseWriter, *Request)
是一個被定義成func(ResponseWriter, *Request)型別的自定義函式。
logging這個中介軟體實現的功能:
logging在返回的HandlerFunc型別函式裡,首先把請求的URL路徑寫入log中,然後再呼叫傳入的Handler(foo、bar)來處理真正的邏輯。從這裡就可以看到中介軟體的奧義:在執行Handler的邏輯之前先幹了點別的事情。
多中介軟體
接下來是多中介軟體,下面這個例子實現了在呼叫一個Handler之前/之後呼叫另一個Handler,形成多中介軟體的連線:
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)
}
首先,
type Middleware func(http.HandlerFunc) http.HandlerFunc
將Middleware定義為func(http.HandlerFunc) http.HandlerFunc的函式型別,
而Logging和Method函式作為包裝Middleware的函式,都把Middleware函式型別作為返回值,在返回的Middleware函式中再進一步地呼叫Handler邏輯。
Chain函式則將一個個包裝Middleware的函式再包裝起來,像套娃一樣一層層巢狀,實現多箇中間件的連結。