深入解析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) Handler
    if 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不是函数就直接panic
      panic("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 = params
   c.handlers = make([]Handler, 0, len(r.m.handlers)+len(handlers))
   c.handlers = append(c.handlers, r.m.handlers...)//添加全局默认handle
   c.handlers = append(c.handlers, handlers...) //添加用户路由的handle
   c.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 response
		if len(vals) > 0 { //如果返回大于0,然后从返回值里面获取
			ev := ctx.GetVal(reflect.TypeOf(ReturnHandler(nil)))
			handleReturn := ev.Interface().(ReturnHandler) //获取默认的ReturnHandler
			handleReturn(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.Value
      if 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 = 0
		c.handlers = handlers
		c.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"] = start

		rw := 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.Injector
   handlers []Handler
   action   Handler
   index    int

   *Router
   Req    Request
   Resp   ResponseWriter
   params Params
   Render
   Locale
   Data 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 Func
		var argType reflect.Type
		var val reflect.Value
		for 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.Value
	if numIn > 0 {
		in = make([]reflect.Value, numIn)
		var argType reflect.Type
		var val reflect.Value
		for 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 int

func main() {
   m := macaron.Classic()
   var t T1=9999
   m.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.Context
	T1  * T1 `inject:"xx"`
}
type T1 int

func main() {
	m := macaron.Classic()
	var t T1=9999
	m.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应用举例,我会讲解怎么在项目中应用这个函数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值