让gin,echo等golang框架支持结构体注册

·  阅读 1443

背景

gin和echo是golang开发中常用的微框架,但是这两个框架只支持函数注册,不支持结构体注册,比如:

import (
    "gopkg.in/gin-gonic/gin.v1"
    "net/http"
)

func main(){
    router := gin.Default()
    router.GET("/", func(c *gin.Context) {
        c.String(http.StatusOK, "Hello World")
    })
    router.Run(":8000")
}
复制代码

这里是一个路由对应一个handler,但是有时候我们却想让一个路由对应一个结构体实例,通过不同的请求方式,执行结构体中的方法,这该怎么实现呢?下面我们以echo为例进行改造(gin 同理)

定义

改造应该尽量不破坏现有框架的结构,尽量复用现有框架的方法.

为了达到我们想要的效果,需要定义以下的内容:

  1. Handler 接口,注册的结构体应该实现此接口,通过Handle方法来判断请求应该执行哪个结构体方法
type Handler interface {
	Handle(Context) error
}

HandlerFunc func(Context) error   //这个是Echo有的

func (h HandlerFunc) Handle(ctx Context) error {
	 return h(ctx)
}
复制代码
  1. 请求方法对应的接口,在进行注册的时候会使用
type GetHandle interface {
	Get(ctx Context) error
}

type PostHandle interface {
	Post(ctx Context) error
}

.....//其他的一些方法

复制代码
  1. 用于存储结构体注册信息的组件
type Component struct {
	funcs map[string]map[string]interface{} //按照路由,请求方法,和具体的handler进行存储
}

复制代码

这个组件应该放到Echo结构体中,初始化Echo结构的时候就要初始化Component,以等待使用. Component会实现上面的Handler接口,这样不需要每个注册的结构体自己去实现

具体实现

首先我们要写出需要注册的结构体


package main

import (
	"fmt"
	"github.com/sereiner/higo"
	"github.com/sereiner/higo/middleware"
	"github.com/sereiner/log"
)

func main() {
	e := higo.New()
	e.Use(middleware.Logger())
	e.Use(middleware.Limit())
	e.Micro("/", NewTA)   //这个需要自己实现
	log.Fatal(e.Start(":1323"))
}

func NewTA() *TA {
	return &TA{b:12}
}

type TA struct {
	b int
}
// 实现GetHandle接口
func (t *TA) Get(ctx Context) error {
	fmt.Println("get got it!",t.b)
	return nil
}
// 实现PostHandle接口
func (t *TA) Post(ctx Context) error {
	fmt.Println("post got it!",t.b)
	return nil
}

复制代码

在echo中新增两个个方法

// Micro 用于注册
func (e *Echo) Micro(path string, newHandle interface{}, middleware ...MiddlewareFunc) []*Route {
    //这里需要反射获取newHandle的信息,判断是否为函数,参数个数等,我就不写了
    return e.Any(path,e.GetHandlerFunc(path,newHandle),middleware...)
}
// GetHandlerFunc 获取结构体中的信息并进行转换
func (e *Echo) GetHandlerFunc(path string,h interface{}) HandlerFunc {
	tV := reflect.ValueOf(h)
	hn := tV.Call(nil)[0].Interface()

	switch  handler:= hn.(type) {
	case GetHandle:
		var f HandlerFunc = handler.Get
		//将具体方法存入Component
		e.Component.AddHandler(path,GET,f)
	}
	
	switch  handler:= hn.(type) {
	case PostHandle:
		var f HandlerFunc = handler.Post
		e.Component.AddHandler(path,POST,f)
	}
	
	//其他的请求类型....
    
        // 构造一个HandlerFunc
	return func(ctx Context) error {
		return e.Component.Handle(ctx)
	}
}

复制代码

最后是Component的实现,直接给代码

type Component struct {
	funcs map[string]map[string]interface{} 
}

func NewComponent() *Component {
	return &Component{funcs:make(map[string]map[string]interface{})}
}
// Handle 实现了Handler接口
func (c *Component) Handle(ctx Context) error {
	h  := c.GetHandler(ctx.Request().URL.Path,ctx.Request().Method)
	if h == nil {
		ctx.Response().Status = 404
		return nil
	}
	return h(ctx)
}

func (c *Component) AddHandler(path string,method string,h interface{}) {
	_ , ok := c.funcs[path]
	if !ok {
		s := make(map[string]interface{})
		c.funcs[path] = s
	}
	_,ok = c.funcs[path][method]
	if ok {
		fmt.Println("已经注册过了")
		return
	}

	c.funcs[path][method] = h
}

func (c *Component) GetHandler(path string,method string) HandlerFunc {

	sc ,ok :=c.funcs[path]
	if !ok {
		return nil
	}

	h,ok := sc[method]
	if ok {
		return h.(HandlerFunc)
	}
	return nil
}

复制代码

这里只是给出了参考代码,具体的代码还需要深化

最后

成功运行,并为外提供服务

完整的代码参考

分类:
后端
标签:
收藏成功!
已添加到「」, 点击更改