[关闭]
@aliasliyu4 2018-09-28T16:53:13.000000Z 字数 3226 阅读 1323

golang web中间件(2)

一个可以用于实战的web中间件怎么写?如果对于http handler基础不熟悉的,可以参考我之前的文章 https://www.zybuluo.com/aliasliyu4/note/1276075

定义属于你的MyHandler接口, 任何实现其ServeHTTP方法的类型都可以当作是MyHandler。
  1. type MyHandler interface {
  2. ServeHTTP(w http.ResponserWriter, r *http.Request, next http.HandlerFunc)
  3. }
  4. next是下一个需要执行的http Handler,它作为一个参数被传递进去。
仿照net/http定义一个函数类型MyHandlerFunc, 实现ServeHTTP方法。
  1. type MyHandlerFunc func (w http.ResponseWriter, r *http.Request, next http.HandlerFunc)
  2. func (h MyHandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
  3. h(w, r, next) // 调用ServeHTTP就是执行具体的MyHandlerFunc, 需要手动执行next函数
  4. }
定义一个中间件middleware, middlerware的数据结构像是一个单向链表,可以包裹n个前置服务,也可以将需要执行的逻辑放在最后的位置。
  1. type middleware struct {
  2. handler MyHandler // 当前Handler
  3. next middleware // 下一个需要执行的middleware
  4. }
  5. // 实现ServeHTTP方法,也就是实现了http Handler.
  6. func (m *middlerware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  7. // m.next.ServeHTTP 自己调用自己,可以理解成进入下一个middleware
  8. m.handler.ServeHTTP(w, r, m.next.ServeHTTP)
  9. }

如上,midllerware就是一个Handler, 可在mux中注册,我们举个例子:

  1. package main
  2. import (
  3. "fmt"
  4. "net/http"
  5. )
  6. type MyHandler interface {
  7. ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc)
  8. }
  9. type MyHandlerFunc func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc)
  10. func (h MyHandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
  11. h(w, r, next) // 调用ServerHTTP就是执行具体的MyHandlerFunc, 需要手动执行next函数
  12. }
  13. type middleware struct {
  14. handler MyHandler // 当前Handler
  15. next *middleware // 下一个需要执行的middleware
  16. }
  17. // 实现ServeHTTP方法,也就是实现了http Handler.
  18. func (m middleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  19. // m.next.ServeHTTP 自己调用自己,可以理解成进入下一个middleware
  20. m.handler.ServeHTTP(w, r, m.next.ServeHTTP)
  21. }
  22. func build(myHandlers []MyHandler) middleware {
  23. var next middleware
  24. if len(myHandlers) == 0 {
  25. return voidMiddleware()
  26. } else if len(myHandlers) > 1 {
  27. next = build(myHandlers[1:])
  28. } else {
  29. next = voidMiddleware()
  30. }
  31. return middleware{myHandlers[0], &next}
  32. }
  33. func voidMiddleware() middleware {
  34. return middleware{
  35. MyHandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {}),
  36. &middleware{},
  37. }
  38. }
  39. var myHandlerOne = MyHandlerFunc(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
  40. fmt.Println("use myHandlerOne")
  41. next(w, r)
  42. })
  43. var myHandlerTwo = MyHandlerFunc(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
  44. fmt.Println("use myHandlerTwo")
  45. next(w, r)
  46. })
  47. var loveFunc = func(w http.ResponseWriter, r *http.Request) {
  48. fmt.Println("you come you see")
  49. w.Write([]byte("i love golang"))
  50. }
  51. func get(handler http.HandlerFunc) MyHandlerFunc {
  52. return func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
  53. handler.ServeHTTP(w, r) // http.HandlerFunc实际调用f(w, r)
  54. next(w, r) // 后面执行的handler,当然你也写成next.ServeHTTP(w, r),这和next(w, r)本质是一样的
  55. }
  56. }
  57. func main() {
  58. myHandlers := []MyHandler{myHandlerOne, myHandlerTwo, get(loveFunc)}
  59. m := build(myHandlers)
  60. mux := http.NewServeMux()
  61. mux.Handle("/youqu", m)
  62. http.ListenAndServe(":1234", mux)
  63. }
看看效果

为什么要手动执行一下next(w, r)

显然我们看到myHandlerTwo,myHandlerTwo在函数的结尾处调用next(w, r), 其传参数为m.next.ServeHTTP,其本身为函数类型即:func, m.handler.ServeHTTP(w, r, m.next.ServeHTTP), 这里做了匿名转换,将其转换为http.HandlerFunc, 当然显示的转换是更加容易理解的, m.handler.ServeHTTP(w, r, http.HandlerFunc(m.next.ServeHTTP))。 当我们执行next(w, r)的时候其实执行就是m.next.ServeHTTP(w, r), 由此可见,中间件才能往下执行。

总结

灵魂在于ServeHTTP函数和Handler接口,一个请求进来会调用ServeHTTP, 然后找到对应的handler, 对应下图的handle(w, req, ps)

handle如何执行呢? 他会调用Handler的ServeHTTP, 在我们的例子里就是middlerware的ServeHTTP.

大概中间件就是这样运行起来的, 如果你完全懂了,那就可以实战了。

完结。

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