http-server filter 中间件的设计

3. http-server filter 中间件的设计

http服务的启动过程与上述grpc十分类似,其中亮点是,根据业务需求,http-server封装了mux,在提供基础服务:路由、统一化http参数获取、自动打解包等等等基础上,增加了链式请求过滤器的支持。

3.1 链式http请求过滤的实现

可在http/filter.go中看到针对过滤器和业务处理函数接口的定义

// HandleFunc 业务处理函数接口
type HandleFunc func(controller *GRegisterController) (err error)

// Filter 过滤器(拦截器),根据dispatch处理流程进行上下文拦截处理
type Filter func(controller *GRegisterController, f HandleFunc) (err error)

// Chain 链式过滤器
type Chain []Filter

框架会为http服务分配一个filter链,即上述的Chain结构,

// http处理函数
func getGloryHttpHandler(handler func(*GRegisterController) error, req, rsp interface{}, filters []Filter) func(w http.ResponseWriter, r *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {

		retPkg := rspImpPackage

		// recovery
		...

		// 创建针对当前接口的过滤器chain
		chain := Chain{}
		chain.AddFilter(filters) // 注册过滤器

		tRegisterController := GRegisterController{
			Ctx: r.Context(),
			R:   r,
		}
		// 处理 req
		// 处理 rsp
			// 执行业务函数
		if err := chain.Handle(&tRegisterController, handler); err != nil {
			retPkg.SetErrorPkg(w, err)
			return
		}
		// 最终回包
    ...
		return
	}
}

可见,在上述函数中,将用户传入的自定义filter过滤函数,放在chain内形成filter链,再由chain调用Handle 函数,逐个执行链内所有filter,通过所有过滤的请求才最终被执行业务逻辑,否则按照filter逻辑返回。

http/filter.go: Handle函数内的实现

//多个Filter,递归执行
	lastI := n - 1
	return func(controller *GRegisterController, f HandleFunc) error {

		var (
			chainFunc HandleFunc
			curI      int
		)
		chainFunc = func(controller *GRegisterController) error {
			if curI == lastI {
				return f(controller)
			}
			curI++
			err := (*fc)[curI](controller, chainFunc)
			curI--
			return err
		}
		return (*fc)[0](controller, chainFunc)
	}(controller, f)

3.2 链式http请求过滤的使用

// 测试用filter 如果input字段为-2则报错
func myFilter2(controller *ghttp.GRegisterController, f ghttp.HandleFunc) error {
	req, ok := controller.Req.(*gloryHttpReq)
	if !ok {
		log.Error("req type err")
		return errors.New("req type err")
	}

	if req.Input[0] == -2 {
		log.Error("filting because input == -2")
		return errors.New("filting because input == -2")
	}
	err := f(controller)
	return err
}

func main() {
	gloryServer := glory.NewServer()
	httpService := service.NewHttpService("httpDemo")
	httpService.RegisterRouter("/testwithfilter/{hello}/{hello2}", testHandler, &gloryHttpReq{}, &gloryHttpRsp{}, "POST", myFilter1, myFilter2)
	gloryServer.RegisterService(httpService)
	gloryServer.Run()
}

可按照此方法,将,myfilter1,myfilter2等过滤器注册在httpService上,从而针对请求进行过滤。

在GoOnline项目中,此filter被广泛应用于auth部分token鉴权。