深入解析go web框架macaron 二 中间件

文章接上回,来看看框架是怎么执行handler 以及一些其他中间件的

中间件格式

func(resp http.ResponseWriter, req *http.Request)
func(ctx *macaron.Context)     
func(resp http.ResponseWriter, req *http.Request,ctx *macaron.Context)

是不是比其他web框架灵活多了(* ̄︶ ̄)

还可以自定义格式

比如先调用c.Map(value),那么中间件就支持这种形式

func(value *valueType)    

中间件如何被注册的

回到get 方法

// Get is a shortcut for r.Handle("GET", pattern, handlers)
func (r *Router) Get(pattern string, h ...Handler) (leaf *Route) {leaf = r.Handle("GET", pattern, h)if r.autoHead {r.Head(pattern, h...)}return leaf
}

在handle方法中有这么一步

handlers = validateAndWrapHandlers(handlers, r.handlerWrapper)

该方法是将传进来的实例进行验证和包装

// validateAndWrapHandlers preforms validation and wrapping for each input handler.
// It accepts an optional wrapper function to perform custom wrapping on handlers.
func validateAndWrapHandlers(handlers []Handler, wrappers ...func(Handler) Handler) []Handler {var wrapper func(Handler) Handlerif len(wrappers) > 0 {wrapper = wrappers[0]}
​wrappedHandlers := make([]Handler, len(handlers))for i, h := range handlers {h = validateAndWrapHandler(h)if wrapper != nil && !inject.IsFastInvoker(h) {h = wrapper(h)}wrappedHandlers[i] = h}
​return wrappedHandlers
}

validateAndWrapHandler 实现

// validateAndWrapHandler makes sure a handler is a callable function, it panics if not.
// When the handler is also potential to be any built-in inject.FastInvoker,
// it wraps the handler automatically to have some performance gain.
func validateAndWrapHandler(h Handler) Handler {if reflect.TypeOf(h).Kind() != reflect.Func { //handler不是函数就直接panicpanic("Macaron handler must be a callable function")}
​if !inject.IsFastInvoker(h) { //如果不是fastinvoker 类型就进行下面的转换,如果下面类型也不是直接返回switch v := h.(type) {case func(*Context):return ContextInvoker(v)case func(*Context, *log.Logger):return LoggerInvoker(v)case func(http.ResponseWriter, *http.Request):return handlerFuncInvoker(v)case func(http.ResponseWriter, error):return internalServerErrorInvoker(v)}}return h
}

IsFastInvoker 判断

// IsFastInvoker check interface is FastInvoker
func IsFastInvoker(h interface{}) bool {_, ok := h.(FastInvoker)return ok
}
type FastInvoker interface {// Invoke attempts to call the ordinary functions. If f is a function// with the appropriate signature, f.Invoke([]interface{}) is a Call that calls f.// Returns a slice of reflect.Value representing the returned values of the function.// Returns an error if the injection fails.Invoke([]interface{}) ([]reflect.Value, error)
}

如果接口实现了Invoke这个方法,那么他就是FastInvoker,下面再看看handler是怎么被包装的

ContextInvoker

// ContextInvoker is an inject.FastInvoker wrapper of func(ctx *Context).
type ContextInvoker func(ctx *Context)
​
// Invoke implements inject.FastInvoker which simplifies calls of `func(ctx *Context)` function.
func (invoke ContextInvoker) Invoke(params []interface{}) ([]reflect.Value, error) {invoke(params[0].(*Context))return nil, nil
}

handlerFuncInvoker

// handlerFuncInvoker is an inject.FastInvoker wrapper of func(http.ResponseWriter, *http.Request).
type handlerFuncInvoker func(http.ResponseWriter, *http.Request)func (invoke handlerFuncInvoker) Invoke(params []interface{}) ([]reflect.Value, error) {invoke(params[0].(http.ResponseWriter), params[1].(*http.Request))return nil, nil
}
  • 如果用户传进来的handler是这几种,那么转成相对应的函数,如果没有的话则直接返回handler

  • 注意返回值是nil说明他们将来都只能作为中间件,没有返回值

中间件执行方式

func(resp http.ResponseWriter, req *http.Request, params Params) {c := r.m.createContext(resp, req)c.params = paramsc.handlers = make([]Handler, 0, len(r.m.handlers)+len(handlers))c.handlers = append(c.handlers, r.m.handlers...)//添加全局默认handlec.handlers = append(c.handlers, handlers...) //添加用户路由的handlec.run() //执行中间件
}

context 结构

context.run()

func (ctx *Context) run() {for ctx.index <= len(ctx.handlers) {vals, err := ctx.Invoke(ctx.handler())if err != nil {panic(err)}ctx.index++// if the handler returned something, write it to the http responseif len(vals) > 0 { //如果返回大于0,然后从返回值里面获取ev := ctx.GetVal(reflect.TypeOf(ReturnHandler(nil)))handleReturn := ev.Interface().(ReturnHandler) //获取默认的ReturnHandlerhandleReturn(ctx, vals)}if ctx.Written() {return}}
}
  • 该方法跟所有web框架执行中间件的方法一样,循环遍历handler,每执行一个,索引+1

  • ctx.handler() 获取当前未执行索引的中间件,当中间件执行完毕后,执行action,action也是个handler,只是一个特殊的handler在中间件执行完毕后才会执行,一般是个业务 处理函数。

func (ctx *Context) handler() Handler {if ctx.index < len(ctx.handlers) {return ctx.handlers[ctx.index]}if ctx.index == len(ctx.handlers) {return ctx.action}panic("invalid index for context handler")
}

action 设置方法

// Action sets the handler that will be called after all the middleware has been invoked.
// This is set to macaron.Router in a macaron.Classic().
func (m *Macaron) Action(handler Handler) {handler = validateAndWrapHandler(handler)m.action = handler
}
  • GetVal从一个类型和值对应得map里面取值,后面会讲讲这个injecter,框架会用Map方法把一些值映射进这个map里面,key 为反射获取的类型,value为反射获取的值,到时候根据值去取就行了

  • 如果某个handler 会有向前端写数据的操作,那么ctx.Written()就会为true,然后返回

ReturnHandler

该方法是处理调用业务的返回值得,vals为调用的值

func defaultReturnHandler() ReturnHandler {return func(ctx *Context, vals []reflect.Value) {rv := ctx.GetVal(inject.InterfaceOf((*http.ResponseWriter)(nil)))resp := rv.Interface().(http.ResponseWriter)//从injector 里面获取resp 的实例var respVal reflect.Valueif len(vals) > 1 && vals[0].Kind() == reflect.Int {//如果vals 长度>1,并且第一个参数是int,那么这个就是状态码,调用resp.WriteHeader写入状态码resp.WriteHeader(int(vals[0].Int()))respVal = vals[1]} else if len(vals) > 0 {respVal = vals[0]if isError(respVal) {err := respVal.Interface().(error) //如果该第一个响应值是错误,直接响应服务器错误if err != nil {ctx.internalServerError(ctx, err)}return} else if canDeref(respVal) {if respVal.IsNil() {return // Ignore nil error}}}if canDeref(respVal) {respVal = respVal.Elem()}if isByteSlice(respVal) {_, _ = resp.Write(respVal.Bytes())} else {_, _ = resp.Write([]byte(respVal.String()))}}
}
  • 先获取响应值,看第一个值是不是int类型,如果是,写入这个值作为状态码,不是走下面分支

  • 如果第一个值是error,直接响应服务器错误,internalServerError是个回调可以自己设置

    internalServerError func(*Context, error)

  • 如果是指针或者是接口,则获取respVal的值

  • 最后判断是不是字节切片,如果是直接响应,如果不是则获取字符串,响应

    internalServerError 结构

// InternalServerError configurates handler which is called when route handler returns
// error. If it is not set, default handler is used.
// Be sure to set 500 response code in your handler.
func (r *Router) InternalServerError(handlers ...Handler) {handlers = validateAndWrapHandlers(handlers)r.internalServerError = func(c *Context, err error) {c.index = 0c.handlers = handlersc.Map(err)c.run()}
}

context.Next()

中间件的精髓就在这里

// Next runs the next handler in the context chain
func (ctx *Context) Next() {ctx.index++ctx.run()
}

举个例子,如果这样写一个中间件,那肯定是按顺序执行

func main() {m := macaron.Classic()m.Get("/", func(ctx *macaron.Context) {fmt.Println("middleWare1")}, func() string {fmt.Println("hello1")return "Hello world!"})m.Run()
}

请求 / 时,打印结果

middleWare1
hello1

如果加个next了,相当于递归调用,套娃执行

func main() {m := macaron.Classic()m.Get("/", func(ctx *macaron.Context) {fmt.Println("middleWare1")ctx.Next()fmt.Println("middleWare2")}, func() string {fmt.Println("hello1")return "Hello world!"})m.Run()
}

最后打印结果

middleWare1
hello1
middleWare2

画个图描述执行过程

 

当中间件调next就会这样按照图中序号套娃执行,在实际中间件应用中,如果想让其他中间件先执行,在后面再执行代码,那么就得调用context.Next进行执行,比如说计算业务代码的耗时等等。

举个例子,计算耗时的中间件

func Logger() macaron.Handler {return func(res http.ResponseWriter, req *http.Request, c *macaron.Context) {start := time.Now()c.Data["perfmon.start"] = startrw := res.(macaron.ResponseWriter)c.Next()timeTakenMs := time.Since(start) / time.Millisecond
}

ctx.Invoke

接下来来看看执行中间件的函数

context 是没有这个函数,他继承自inject.Injector

// Context represents the runtime context of current request of Macaron instance.
// It is the integration of most frequently used middlewares and helper methods.
type Context struct {inject.Injectorhandlers []Handleraction   Handlerindex    int*RouterReq    RequestResp   ResponseWriterparams ParamsRenderLocaleData map[string]interface{}
}

当创建context的时候,被赋值为inject.New()

func (m *Macaron) createContext(rw http.ResponseWriter, req *http.Request) *Context {c := &Context{Injector: inject.New(),handlers: m.handlers,action:   m.action,index:    0,Router:   m.Router,Req:      Request{req},Resp:     NewResponseWriter(req.Method, rw),Render:   &DummyRender{rw},Data:     make(map[string]interface{}),}c.SetParent(m)c.Map(c) //将c 映射进去就是map[reflect(c.type)]=refelct(c.value)c.MapTo(c.Resp, (*http.ResponseWriter)(nil))//将http.ResponseWriter类型的值映射换成自己的resp,到时候查找http.ResponseWriter类型的时候,会把自己的resp 传进去c.Map(req)//将请求映射进去return c
}

Injector

创建Injector

// New returns a new Injector.
func New() Injector {return &injector{values: make(map[reflect.Type]reflect.Value),}
}

调用hanlder 的invoke 函数

Invoke

// Invoke attempts to call the interface{} provided as a function,
// providing dependencies for function arguments based on Type.
// Returns a slice of reflect.Value representing the returned values of the function.
// Returns an error if the injection fails.
// It panics if f is not a function
func (inj *injector) Invoke(f interface{}) ([]reflect.Value, error) {t := reflect.TypeOf(f)switch v := f.(type) {case FastInvoker:return inj.fastInvoke(v, t, t.NumIn())default:return inj.callInvoke(f, t, t.NumIn())}
}
  • 先获取handler的类型,如果是前面处理过的类型,就是context一类的,调用fastInvoke,如果都不是调用默认的callInvoke

fastInvoke

func (inj *injector) fastInvoke(f FastInvoker, t reflect.Type, numIn int) ([]reflect.Value, error) {var in []interface{}if numIn > 0 {in = make([]interface{}, numIn) // Panic if t is not kind of Funcvar argType reflect.Typevar val reflect.Valuefor i := 0; i < numIn; i++ {argType = t.In(i)val = inj.GetVal(argType)//遍历传入参数,从map 里面找参数类型对应的值,然后给参数赋值if !val.IsValid() {return nil, fmt.Errorf("Value not found for type %v", argType)}in[i] = val.Interface()}}return f.Invoke(in)
}
  • 直接调用f.Invoke赋值,获取参数,然后调用f.Invoke进行函数调用,就是注册时候的ContextInvoker一类的方法

callInvoke

// callInvoke reflect.Value.Call
func (inj *injector) callInvoke(f interface{}, t reflect.Type, numIn int) ([]reflect.Value, error) {var in []reflect.Valueif numIn > 0 {in = make([]reflect.Value, numIn)var argType reflect.Typevar val reflect.Valuefor i := 0; i < numIn; i++ { //遍历传入参数,从map 里面找参数类型对应的值,然后给参数赋值argType = t.In(i)val = inj.GetVal(argType)if !val.IsValid() {return nil, fmt.Errorf("Value not found for type %v", argType)}in[i] = val}}return reflect.ValueOf(f).Call(in), nil //反射调用handler,将获取的值作为参数传进去
}
  • 遍历handler,如果handler的参数>0,然后遍历参数,从map 里面取出该类型的值,前提是要通过Map 方法映射进去,然后将该参数赋值,最后通过反射调用handler,将获得值传进去,这就能解释为什么,框架的中间件可以有很多种类型,比较灵活了

Map

map 负责将值的类型和值对应起来

// Maps the concrete value of val to its dynamic type using reflect.TypeOf,
// It returns the TypeMapper registered in.
func (i *injector) Map(val interface{}) TypeMapper {i.values[reflect.TypeOf(val)] = reflect.ValueOf(val)return i
}

下面举个例子介绍map方法怎么使用,下面的例子将T1 类型的值映射进去,在中间件里面写上func(t1*T1)形式就可以获取值,有点依赖注入的味道

type T1 intfunc main() {m := macaron.Classic()var t T1=9999m.Map(&t)m.Get("/hello", func(t1*T1) {fmt.Println(*t1) //打印9999}, myHandler)log.Println("Server is running...")log.Println(http.ListenAndServe("0.0.0.0:5000", m))
}

Apply

该方法将结构的字段映射进去,如果结构体后面tag为inject

// Maps dependencies in the Type map to each field in the struct
// that is tagged with 'inject'.
// Returns an error if the injection fails.
func (inj *injector) Apply(val interface{}) error {v := reflect.ValueOf(val)for v.Kind() == reflect.Ptr {v = v.Elem()}if v.Kind() != reflect.Struct {return nil // Should not panic here ?}t := v.Type()for i := 0; i < v.NumField(); i++ {f := v.Field(i)structField := t.Field(i)if f.CanSet() && (structField.Tag == "inject" || structField.Tag.Get("inject") != "") {ft := f.Type()v := inj.GetVal(ft)if !v.IsValid() {return fmt.Errorf("Value not found for type %v", ft)}f.Set(v)}}return nil
}

举个例子验证下apply 的应用

type Test struct {T  * macaron.ContextT1  * T1 `inject:"xx"`
}
type T1 intfunc main() {m := macaron.Classic()var t T1=9999m.Map(&t)var t2 =&Test{}err:=m.Apply(t2)if err!=nil{panic(err)}fmt.Println(*t2.T1)m.Get("/hello", func(ctx * macaron.Context) {}, myHandler)log.Println("Server is running...")log.Println(http.ListenAndServe("0.0.0.0:5000", m))
}func myHandler(ctx *macaron.Context) string {return "the request path is: " + ctx.Req.RequestURI
}
  • apply 将结构体放进去,如果结构体字段被打tag:inject,那么结构体这个字段将会通过f.Set(v)被赋上值。在后面grafana应用举例,我会讲解怎么在项目中应用这个函数。


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部