目录

项目中gin封装

背景

项目中使用gin框架,一般的注册路由和函数都这样写

func main() {
	engine := gin.New()
	engine.GET("/test",testHandler)
	engine.Run("localhost:8080")
}

type TestReq struct {
	Name string
}
func testHandler(ctx *gin.Context)  {
	// 解析参数
	name:=ctx.Query("name")
	testReq:=TestReq{Name: name}
	
	// 检查参数
	checkTestReq(testReq)
	...
	// 获取session
	sessionId:=ctx.GetHeader("Session-Id")
	...
	
}

在这里我们只写一些核心伪代码。 我们经常要再hanler里去解析参数,校验参数,获取登录者的session,这样才能校验登录拿到userId等操作。

每一个hanler里面基本都要写一套这样的东西,好烦! 当然我们可以把这一块封装一下,把解析参数、检查参数、获取session封装个函数,然后在各个handler调用一下不就好了吗? 可以,但是每次都调用一下也够的。

如果我们每次都只处理这样的函数就好了:

func MyTestHandler(ctx *gin.Context,userId int,req *TestReq)  {
	// req是已经过校验合法的
	// 安心处理自己的逻辑
}

修改注册路由

我们首先要修改路由注册原始的方法。

func main() {
	engine := gin.New()
	engine.GET("/test",GenHandlerFunc(MyTestHandler))
	engine.Run("localhost:8080")
}


func GenHandlerFunc(myHandlerFunc interface{}) gin.HandlerFunc {
	return func(context *gin.Context) {
		// 代码1处。调用myHandlerFunc
	}
}

func MyTestHandler(ctx *gin.Context,userId int,req *TestReq)  {
	// req是已经过校验合法的
	// 安心处理自己的逻辑
}

MyTestHandler函数是用着方便了,可是GenHandlerFunc可麻烦了。代码1处调用myHandlerFunc谈何容易。

以后有越来越来多的MyTestHandler,每个接口都有自己的req类型,怎么能动态填充千变万化的req参数呢?

封装

首先为了校验req参数是否合法,我们定义一个参数,每个req结构体都要实现该方法

type ValidateAble interface {
	Valid() error
}

type TestReq struct {
	Name string `form:"name"`
}

func (req *TestReq) Valid() error {
	if req.Name==""{
		return errors.New("name is empty")
	}
	
	return nil
}
func GenHandlerFunc(myHandlerFunc interface{}) gin.HandlerFunc {
	return func(context *gin.Context) {
		// 反射函数
		controllerFuncType := reflect.TypeOf(myHandlerFunc)
		if controllerFuncType.Kind() != reflect.Func {
			panic( "controllerFunc not a func")
		}

		// 调用函数所需要的参数
		args := make([]reflect.Value, 0)
		args = append(args, reflect.ValueOf(context))
		// 为了方便我们假设userId是10,开发中根据自己业务去取
		args = append(args, reflect.ValueOf(10))
		for i := 0; i < controllerFuncType.NumIn(); i++ {
			inParam := controllerFuncType.In(i)
			// 判断req是否实现了ValidateAble接口
			if !inParam.Implements(reflect.TypeOf((*ValidateAble)(nil)).Elem()) {
				continue
			}
			inParamVale := reflect.New(inParam.Elem())
			req, ok := inParamVale.Interface().(ValidateAble)
			if !ok {
				panic(context.Request.URL.Path + " req  convert requestutil.ValidateAble err")
			}
			err:=context.ShouldBind(req)
			fmt.Println(req)
			if err != nil {
				fmt.Println(err)
			}
			err = req.Valid()
			if err != nil {
				context.JSON(404,gin.H{
					"message":"参数错误",
				})
				return
			}
			args = append(args, reflect.ValueOf(req))
		}
		reflect.ValueOf(myHandlerFunc).Call(args)
	}
}

其实核心代码是inParamVale := reflect.New(inParam.Elem())

我们看下reflect.New()方法

// New returns a Value representing a pointer to a new zero value
// for the specified type. That is, the returned Value's Type is PtrTo(typ).
func New(typ Type) Value {
	if typ == nil {
		panic("reflect: New(nil)")
	}
	t := typ.(*rtype)
	ptr := unsafe_New(t)
	fl := flag(Ptr)
	return Value{t.ptrTo(), ptr, fl}
}

我们只要看注释就行了,写的很明白, 返回一个零值的结构体的指针。虽然我们不知道到底需要哪个类型的req,没关系。