1. 程式人生 > 其它 >Go語言中介軟體(Middleware)

Go語言中介軟體(Middleware)

技術標籤:實習記錄go中介軟體

首先,什麼是Handler?

簡單來說,go Web通過http.HandleFunc()來註冊預設路由,將傳入URL匹配到相應的Handler。
它的函式原型為:

http.HandleFunc(pattern string, handler func(ResponseWriter, *Request))

其中,Handler是我們處理請求和生成返回資訊邏輯處理函式。

什麼是中介軟體呢?

中介軟體(MiddleWare)實際上就是一個返回值為Handler的中間處理函式。

中介軟體有啥用呢?

有時候在執行實際Handler裡面的邏輯的時候想要預處理或者後處理一些行為(比如寫入log、統計執行時間等等);有時候我們想要在呼叫一個Handler之前或之後呼叫另一個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的函式再包裝起來,像套娃一樣一層層巢狀,實現多箇中間件的連結。