讓gin,echo等golang框架支援結構體註冊

櫻桃老腰子發表於2019-04-13

背景

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
}

複製程式碼

這裡只是給出了參考程式碼,具體的程式碼還需要深化

最後

讓gin,echo等golang框架支援結構體註冊
成功執行,併為外提供服務

完整的程式碼參考

相關文章