【02-中間件】構建go web框架

【02-中間件】構建go web框架

以前咱們項目中遇到的問題是代碼重複。在處理請求以前,咱們一般須要進行日誌記錄,異常捕獲,用戶認證等操做。而且這些操做須要被應用到每個處理handler中。golang

使用golang的基礎包net/http建立了一個很是簡單的應用web

import "net/http"

type DefaultHandler struct {}

func (DefaultHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)  {
    path := r.URL.Path
    _, _ = w.Write([]byte(path + "  wellcome to http server."))
}

func userLogin(w http.ResponseWriter, r *http.Request)  {
    path := r.URL.Path
    _, _ = w.Write([]byte(path + "  wellcome to http server by handleFunc."))
}

func main() {

    http.Handle("/", DefaultHandler{})
    http.HandleFunc("/apis", userLogin)

    _ = http.ListenAndServe("0.0.0.0:8080", nil)
}

http.Handle 接受兩個參數,第二個參數類型是http.Handler, 它是一個接口類型包含了ServeHTTP(ResponseWriter, *Request)方法,因此任何實現了該方法的類型,均可以看成http.Handler來使用,傳入http.Handle 方法中。api

如今,咱們想要記錄每一個請求的耗時:框架

func userLogin(w http.ResponseWriter, r *http.Request)  {

    t := time.Now()
    path := r.URL.Path
    _, _ = w.Write([]byte(r.Method + path + "  wellcome to http server by handleFunc."))
    t2 := time.Now()
    log.Printf("[%s] %q %v\n", r.Method, r.URL.String(), t2.Sub(t))

}

對於一個handler 目前的處理辦法仍是可以知足, 可是當有更多的handler方法時,就顯得很麻煩了。函數

鏈式handlers 概念

咱們想要相似其餘系統中的中間件系統。對於golang標準庫已經有這種handler http.StripPrefix(prefix, handler) and http.TimeoutHandler(handler, duration, message) 。 他們都把一個handler 當作參數,而且返回值也是handler。這樣咱們就能夠將一個handler嵌套在另一個handler中,並按照下面的方式來使用它了。
loggingHandler(recoverHandler(indexHandler))日誌

Alice

Alice 就是完成鏈式handler的包,只是她提供的使用方法變得更加優雅。code

func main() {
  commonHandlers := alice.New(loggingHandler, recoverHandler)
  http.Handle("/about", commonHandlers.ThenFunc(aboutHandler))
  http.Handle("/", alice.New(commonHandlers, bodyParserHandler).ThenFunc(indexHandler))
  http.ListenAndServe(":8080", nil)
}

多參數鏈式handler

有了alice,咱們仍是沒法使用http.StripPrefix(prefix, handler)這樣的handler由於它不是 func (http.Handler) http.Handler類型的方法,每次須要使用這種handler時,須要對其進行封裝server

func myStripPrefix(h http.Handler) http.Handler {
    return http.StripPrefix("/old", h)
}

回到咱們的日誌handler

咱們把兩個處理函數改成返回 方法+path中間件

func userLogin(w http.ResponseWriter, r *http.Request)  {
    w.Write([]byte(r.URL.Path))
}

func userLogin2(w http.ResponseWriter, r *http.Request)  {
    w.Write([]byte(r.URL.Path))
}

在添加一個處理日誌記錄的方法接口

func logHandler(next http.HandlerFunc) http.HandlerFunc {
    fn := func(w http.ResponseWriter, r *http.Request) {
        log.Printf("%s %s", r.Method, r.URL.Path)
        next.ServeHTTP(w, r)
    }
    return http.HandlerFunc(fn)
}

使用

http.Handle("/login/1", logHandler(userLogin))
http.HandleFunc("/login/2", logHandler(userLogin2))
相關文章
相關標籤/搜索