背景
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 同理)
定義
改造應該儘量不破壞現有框架的結構,儘量複用現有框架的方法.
為了達到我們想要的效果,需要定義以下的內容:
- Handler 介面,註冊的結構體應該實現此介面,通過Handle方法來判斷請求應該執行哪個結構體方法
type Handler interface {
Handle(Context) error
}
HandlerFunc func(Context) error //這個是Echo有的
func (h HandlerFunc) Handle(ctx Context) error {
return h(ctx)
}
複製程式碼
- 請求方法對應的介面,在進行註冊的時候會使用
type GetHandle interface {
Get(ctx Context) error
}
type PostHandle interface {
Post(ctx Context) error
}
.....//其他的一些方法
複製程式碼
- 用於儲存結構體註冊資訊的元件
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
}
複製程式碼
這裡只是給出了參考程式碼,具體的程式碼還需要深化