web开发的背景下,“中间件”通常意思是“包装原始应用并添加一些额外的功能的应用的一部分”。这个概念似乎总是不被人理解,但是我认为中间件非常棒。

首先,一个好的中间件有一个责任就是可插拔并且自足。这就意味着你可以在接口级别嵌入你的中间件他就能直接运行。它不会影响你编码方式,不是框架,仅仅是你请求处理里面的一层而已。完全没必要重写你的代码,如果你想使用中间件的一个功能,你就帮他插入到那里,如果不想使用了,就可以直接移除。

纵观Go语言,中间件是非常普遍的,即使在标准库中。虽然开始的时候不会那么明显,在标准库net/http中的函数StripText或者TimeoutHandler就是我们要定义和的中间件的样子,处理请求和相应的时候他们包装你的handler,并处理一些额外的步骤。

一开始,我们认为编写中间件似乎很容易,但是我们实际编写的时候也会遇到各种各样的坑。让我们来看看一些例子。

代码实现

//middleware/slice_router.go
package middleware

import (
    "context"
    "math"
    "net/http"
    "strings"
)

//目标定位是 tcp、http通用的中间件
//知其然也知其所以然

const abortIndex int8 = math.MaxInt8 / 2 //最多 63 个中间件

type HandlerFunc func(*SliceRouterContext)

// router 结构体
type SliceRouter struct {
    groups []*SliceGroup
}

// group 结构体
type SliceGroup struct {
    *SliceRouter
    path     string
    handlers []HandlerFunc
}

// router上下文
type SliceRouterContext struct {
    Rw  http.ResponseWriter
    Req *http.Request
    Ctx context.Context
    *SliceGroup
    index int8
}

func newSliceRouterContext(rw http.ResponseWriter, req *http.Request, r *SliceRouter) *SliceRouterContext {
    newSliceGroup := &SliceGroup{}

    //最长url前缀匹配
    matchUrlLen := 0
    for _, group := range r.groups {
        //fmt.Println("req.RequestURI")
        //fmt.Println(req.RequestURI)
        if strings.HasPrefix(req.RequestURI, group.path) {
            pathLen := len(group.path)
            if pathLen > matchUrlLen {
                matchUrlLen = pathLen
                *newSliceGroup = *group //浅拷贝数组指针
            }
        }
    }

    c := &SliceRouterContext{Rw: rw, Req: req, SliceGroup: newSliceGroup, Ctx: req.Context()}
    c.Reset()
    return c
}

func (c *SliceRouterContext) Get(key interface{}) interface{} {
    return c.Ctx.Value(key)
}

func (c *SliceRouterContext) Set(key, val interface{}) {
    c.Ctx = context.WithValue(c.Ctx, key, val)
}

type SliceRouterHandler struct {
    coreFunc func(*SliceRouterContext) http.Handler
    router   *SliceRouter
}

func (w *SliceRouterHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
    c := newSliceRouterContext(rw, req, w.router)
    if w.coreFunc != nil {
        c.handlers = append(c.handlers, func(c *SliceRouterContext) {
            w.coreFunc(c).ServeHTTP(rw, req)
        })
    }
    c.Reset()
    c.Next()
}

func NewSliceRouterHandler(coreFunc func(*SliceRouterContext) http.Handler, router *SliceRouter) *SliceRouterHandler {
    return &SliceRouterHandler{
        coreFunc: coreFunc,
        router:   router,
    }
}

// 构造 router
func NewSliceRouter() *SliceRouter {
    return &SliceRouter{}
}

// 创建 Group
func (g *SliceRouter) Group(path string) *SliceGroup {
    return &SliceGroup{
        SliceRouter: g,
        path:        path,
    }
}

// 构造回调方法
func (g *SliceGroup) Use(middlewares ...HandlerFunc) *SliceGroup {
    g.handlers = append(g.handlers, middlewares...)
    existsFlag := false
    for _, oldGroup := range g.SliceRouter.groups {
        if oldGroup == g {
            existsFlag = true
        }
    }
    if !existsFlag {
        g.SliceRouter.groups = append(g.SliceRouter.groups, g)
    }
    return g
}

// 从最先加入中间件开始回调
func (c *SliceRouterContext) Next() {
    c.index++
    for c.index < int8(len(c.handlers)) {
        //fmt.Println("c.index")
        //fmt.Println(c.index)
        c.handlers[c.index](c)
        c.index++
    }
}

// 跳出中间件方法
func (c *SliceRouterContext) Abort() {
    c.index = abortIndex
}

// 是否跳过了回调
func (c *SliceRouterContext) IsAborted() bool {
    return c.index >= abortIndex
}

// 重置回调
func (c *SliceRouterContext) Reset() {
    c.index = -1
}

代码测试

实现两个中间件

//middleware/tracelog_slicemw.go
package middleware

import (
    "log"
)

func TraceLogSliceMW() func(c *SliceRouterContext) {
    return func(c *SliceRouterContext) {
        log.Println("trace_in")
        c.Next()
        log.Println("trace_out")
    }
}
//middleware/url.go
package middleware

func Url() func(c *SliceRouterContext) {
    return func(c *SliceRouterContext) {
        if c.Req.RequestURI == "/favicon.ico" {
            c.Abort()
        }
    }
}

代码测试

//main.go
package main

import (
    "fmt"
    "net/http"

    "github.com/student/middleware/middleware"
)

func main() {
    //初始化方法数组路由器
    sliceRouter := middleware.NewSliceRouter()
    //中间件可充当业务逻辑代码
    sliceRouter.Group("/").Use(middleware.Url(), middleware.TraceLogSliceMW(), func(c *middleware.SliceRouterContext) {
        fmt.Println("中间件")
    })
    sliceRouter.Group("/topgoer").Use(middleware.Url(), middleware.TraceLogSliceMW(), topgoer)
    routerHandler := middleware.NewSliceRouterHandler(nil, sliceRouter)
    err := http.ListenAndServe(":9090", routerHandler)
    if err != nil {
        fmt.Println("HTTP server failed,err:", err)
        return
    }
}
func topgoer(c *middleware.SliceRouterContext) {
    fmt.Println("www.topgoer.com是个不错的go语言中文文档")
}
文档更新时间: 2021-03-14 19:38   作者:kuteng