程序员GoGolang

golang 网络编程(5)中间件

2019-04-22  本文已影响22人  zidea
golang_real.jpg

当构建 web 应用程序,可能对所有的请求会共享一些功能。例如我们每一个请求都会写入日志。

在 go 语言 net/http 标准库中提供了中间件类似的函数 StripPrefix 和 TimeoutHandler。
如何自己编写一个中间件呢?答案是

func middlewareHandler(next http.Handler) http.Handler {
 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 // 在执行应用处理器前编译中间件的逻辑
 next.ServeHTTP(w, r)
 // 在执行应用处理器后将会执行的编译中间件的逻辑
 })
}

从上面示例我们可以看出 go 语言是支持高阶函数,通过向下传递 http.Handler 来实现中间件。

package main

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

func logginHandler(next http.Handler) http.Handler{
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){
        start := time.Now()
        log.Printf("Started %s %s",r.Method, r.URL.Path)
        next.ServeHTTP(w,r)
        log.Printf("Completed %s in %v", r.URL.Path, time.Since(start))
    })
}

func index(w http.ResponseWriter,r *http.Request ){
    log.Println("Executing index handler")
    fmt.Fprintf(w,"welcome!")
}

func about(w http.ResponseWriter, r *http.Request){
    log.Println("Executing about handler")
    fmt.Fprintf(w,"Go Middleware")
}

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

}

func main()  {

    http.HandleFunc("/favicon.ico", iconHandler)

    indexHandler := http.HandlerFunc(index)
    aboutHandler := http.HandlerFunc(about)

    http.Handle("/",logginHandler(indexHandler))
    http.Handle("/about",logginHandler(aboutHandler))

    server :=  &http.Server{
        Addr:":8080",
    }

    log.Println("Listening...")
    server.ListenAndServe()
}
Listening...
 Started GET /
 Executing index handler
 Completed / in 41.129µs
 Started GET /
 Executing index handler
 Completed / in 50.475µs
 Started GET /about
 Executing about handler
 Completed /about in 49.483µs

许多书都提供代码示例,个人建议是不要 copy 过来运行看效果,自己手 coding 就可以发现一些问题,同时可以思考为什么要这样编写代码。

通过上面示例我们了解如何写一个简单中间件,这个个人看来和 nodejs 实现中间件没有什么差别。实际开发中我们往往会有多个中间件来执行业务,那么这些中间件执行顺序也是我们值得考虑的问题。

package main

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

func middlewareFirst(next http.Handler) http.Handler{
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){
        log.Println("MiddlewareFirst - before Handler")
        next.ServeHTTP(w,r)
        log.Println("MiddlewareFirst - after Handler")
    })
}

func middlewareSecond(next http.Handler) http.Handler{
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){
        log.Println("MiddlewareSecond - before Handler")
        if r.URL.Path == "/message"{
            if r.URL.Query().Get("password") == "123"{
                log.Println("Authorized to system...")
                next.ServeHTTP(w,r)
            }else{
                log.Println("Failed to authorize to the system")
                return
            }
        }else{
            next.ServeHTTP(w,r)
        }
        log.Println("MiddlewareScond - after Handler")      
    })
}

func index(w http.ResponseWriter, r *http.Request){
    log.Println("Executed index Handler")
    fmt.Fprintf(w,"welcome")
}

func message(w http.ResponseWriter, r *http.Request){
    log.Println("Executed message Handler")
    fmt.Fprintf(w,"message...")
}

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

}

func main() {
    http.HandleFunc("/favicon",iconHandler)
    indexHandler := http.HandlerFunc(index)
    messageHandler := http.HandlerFunc(message)

    http.Handle("/",middlewareFirst(middlewareSecond(indexHandler)))
    http.Handle("/message",middlewareFirst(middlewareSecond(messageHandler)))

    server  := &http.Server{
        Addr:":8080",
    }

    log.Println("Listen...")

    server.ListenAndServe()

}

在上面代码中其实也没有什么特别之处,就是我们创建了两个middlewareFirstmiddlewareSecond两个中间件并且有一定先后顺序,然后在 middlewareSecond中写了一个对访问权限校验的逻辑。大家可以尝试地去运行一下。

th-7.jpeg
上一篇 下一篇

猜你喜欢

热点阅读