[关闭]
@aliasliyu4 2018-09-25T14:32:37.000000Z 字数 3623 阅读 1321

go的web中间件

web中间件常见的用途是一些公共方法的执行,譬如,获取用户的信息,压缩等。

在讲这些之前我们先实现一个go的web server。我们知道在go里面实现一个web服务器是相对容易的事情,下面是一个例子:

  1. package main
  2. import (
  3. "net/http"
  4. )
  5. func love(w http.ResponseWriter, r *http.Request) {
  6. w.Write([]byte("i love golang"))
  7. }
  8. func main() {
  9. mux := http.NewServeMux()
  10. mux.HandleFunc("/love", love)
  11. http.ListenAndServe(":1314", mux)
  12. }

在你的终端输入 curl http://:1314/love你会得到下面的输出, 或者在你的浏览器窗口输入http://127.0.0.1/love, 会输出 "i love golang"
iCbIuF.png

Handler

Hanlder在net/http标准包中的定义,可以看到它时一个接口类型,里面包含了一个ServerHTTP的方法,任何实现该方法的类型,都可以被当作时Handler

  1. type Handler interface {
  2. ServeHTTP(ResponseWriter, *Request)
  3. }
  4. // Handle registers the handler for the given pattern.
  5. // If a handler already exists for pattern, Handle panics.
  6. func (mux *ServeMux) Handle(pattern string, handler Handler) { // handler直接可以注册到mux里面
  7. mux.mu.Lock()
  8. defer mux.mu.Unlock()
  9. if pattern == "" {
  10. panic("http: invalid pattern")
  11. }
  12. if handler == nil {
  13. panic("http: nil handler")
  14. }
  15. if _, exist := mux.m[pattern]; exist {
  16. panic("http: multiple registrations for " + pattern)
  17. }
  18. if mux.m == nil {
  19. mux.m = make(map[string]muxEntry)
  20. }
  21. mux.m[pattern] = muxEntry{h: handler, pattern: pattern}
  22. if pattern[0] != '/' {
  23. mux.hosts = true
  24. }
  25. }

mux也叫做router,它的作用是,注册request path和它对应的handler,handler负责处理具体的逻辑,比如获取一个用户的个人信息,或者退出一个系统等。

基于此我们可以写出另外一种形式的mini http server, 下面是一个用自己实现的Handler类型,然后注册到mux,并且监听外部输入的例子:

  1. package main
  2. import (
  3. "net/http"
  4. )
  5. type love struct {
  6. lang string
  7. }
  8. func (l *love) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  9. w.Write([]byte("i love " + l.lang))
  10. }
  11. func main() {
  12. mux := http.NewServeMux()
  13. l := &love{lang: "golang"}
  14. mux.Handle("/handler", l) // l是Hander类型
  15. http.ListenAndServe(":1314", mux)
  16. }

在终端中输入curl http://:1314/handler, 输出如下。

函数即类型 HandlerFunc

golang中函数也可以作为一种类型,HandlerFunc就是绝佳的实践,它实现了ServeHTTP方法,也就是说它属于上面我们说的Handler类型,那么也就是说假如我们有一个函数类似于:f(w, r), 就可以利用类型之间的转换将其升级为(HandlerFunc(f)) HandlerFunc类型,然后其自动继承了ServeHTTP方法。

  1. type HandlerFunc func(ResponseWriter, *Request)
  2. // ServeHTTP calls f(w, r).
  3. func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
  4. f(w, r)
  5. }

写下这个有趣的例子吧。

  1. package main
  2. import (
  3. "net/http"
  4. )
  5. func love(w http.ResponseWriter, r *http.Request) {
  6. w.Write([]byte("i love golang"))
  7. }
  8. func main() {
  9. mux := http.NewServeMux()
  10. l := http.HandlerFunc(love) // 类型变成HandlerFunc, 也可以认为是Handler类型
  11. http.Handle("/handlerfunc",l) // 所以我们这样注册
  12. http.ListenAndServe(":1314", mux)
  13. }
  14. 其实main还这样写
  15. func main() {
  16. mux := http.NewServeMux()
  17. http.HandlerFunc("/handlerfunc", love) // net/http提供了HandlerFunc的直接注册
  18. http.ListenAndServe(":1314", mux)
  19. }

为什么可以这样写呢?下面的代码可以解释, 其实它的内部实现了一个mux.Handle, 并且将我们实现的handler函数转换成Handler,然后完成注册工作。

  1. // HandleFunc registers the handler function for the given pattern.
  2. func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
  3. mux.Handle(pattern, HandlerFunc(handler))
  4. }

DefaultServeMux

mux := http.NewServeMux()我们拿到了一个默认的mux, 它的结构如下,其中mu是一把读写互斥锁, m存储了请求路径对应的Handler。相信这个结构是很清晰明的。

  1. type ServeMux struct {
  2. mu sync.RWMutex
  3. m map[string]muxEntry
  4. hosts bool // whether any patterns contain hostnames
  5. }
  6. type muxEntry struct {
  7. h Handler
  8. pattern string
  9. }

扯了这么久,要说到middlerware怎么写了

假如我们需要在每一个请求的前面都输出一句"我喜欢和朱颖在一起",我们该怎么做?

让我们来试一试吧。

  1. package main
  2. import (
  3. "log"
  4. "net/http"
  5. )
  6. // wantToBeWithYou是一个中间件函数
  7. func wantToBeWithYou(next http.Handler) http.Handler {
  8. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  9. log.Printf("zhuying want to be with you") // 中间业务在这里,可以设置ctx,鉴权等
  10. next.ServeHTTP(w, r) // 这里本质上执行的下面的rain方法
  11. })
  12. }
  13. // 某一个具体的业务Handler
  14. func rain(w http.ResponseWriter, r *http.Request) {
  15. w.Write([]byte("漕河泾正下着淅淅沥沥的秋雨"))
  16. }
  17. func main() {
  18. mux := http.NewServeMux()
  19. h := http.HandlerFunc(rain)
  20. mux.Handle("/middleware", wantToBeWithYou(h))
  21. http.ListenAndServe(":1314", mux)
  22. }
  23. 输出如下
  24. ╰─$ go run test.go 1
  25. 2018/09/07 16:59:23 zhuying want to be with you
  26. 2018/09/07 16:59:23 漕河泾正下着淅淅沥沥的秋雨

如上我们实现了一个简单的中间件功能,我们成功的打印了log信息,并且执行了我们rain方法。一般而言我们会在wantToBeWithYou做一些更具体的事情,比如你可能想在ctx中设置一些value,甚至你想检查session是否合理,这些都可以在middleware中做。

为什么写这些

因为最近在看新公司的代码,gateway这一块的写法比较的原始,遂有这一篇复习气息浓烈的小文,希望对你有帮助。

未完待续!!!!!!

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注