Tango,微內核可擴展的Go語言Web框架
Golang的web框架基本上處于一個井噴期,那么為什么要再造一個輪子。這是因為,目前可擴展性比較強的都是基于函數作為可執行體的,而以結構體作為執行體的框架目前可擴展性都不夠強,包括我原先寫的框架xweb也是如此。因此,一個全新的框架出來了,先上地址:https://github.com/lunny/tango。
初看Tango框架,感覺和Martini及Macaron非常的像。比如這段代碼:
package mainimport "github.com/lunny/tango"
func main() { t := tango.Classic() t.Get("/", func() string { return "Hello tango!" }) t.Run() }</pre>
這種其實大家都支持的。再看這個吧:
package mainimport "github.com/lunny/tango"
type Action struct {} func (Action) Get() string { return "Hello tango!" }
func main() { t := tango.Classic() t.Get("/", new(Action)) t.Run() }</pre>
Tango同時支持函數和結構體作為執行體,不過主打是結構體,函數只作為兼容而用。下面對Tango的各種功能一一道來。
路由
Tango目前同時支持3種路由規則:
- 靜態路由
tg.Get("/", new(Action)) - 命名路由
tg.Get("/:name", new(Action)) 命名路由對應的參數內容可通過ctx.Params().Get(":name")來獲得
- 正則路由
tg.Get("/(.*)", new(Action)) 正則路由對應的參數內容可通過ctx.Params().Get(":0")來獲得
這里要注意命名路由和正則路由不可混用。
執行體
同時支持函數執行體和結構體執行體,支持的函數執行體形式如下,可以有零個或一個返回值,返回值由Return插件提供,后面會再提:
func() func(http.ResponseWriter, *http.Request) func(*tango.Context) func(http.Response.Writer) func(*http.Request)
同時支持包含Get,Post,Head,Options,Trace,Patch,Delete,Put作為成員方法的結構體作為執行體。
type Action struct {}
func (Action) Get() string {
return "Get"
}
func (Action) Post() string {
return "Post"
}
func (Action) Head() string {
return "Head"
}
func (Action) Options() string {
return "Options"
}
func (Action) Trace() string {
return "Trace"
}
func (Action) Patch() string {
return "Patch"
}
func (Action) Delete() string {
return "Delete"
}
func (Action) Put() string {
return "Put"
} 在使用路由時,可以用對應的方法或者Any來加路由:
t := tango.Classic()
t.Get("/1", new(Action))
t.Any("/2", new(Action)) 路由分組
Tango提供了Group來進行路由分組,Group的最簡單形式如下:
g := tango.NewGroup()
g.Get("/1", func() string {
return "/1"
})
g.Post("/2", func() string {
return "/2"
})
t := tango.Classic()
t.Group("/api", g) 這樣訪問/api/1就會返回/1,訪問/api/2就會返回/2
同時也可以這樣:
t := tango.Classic()
t.Group("/api", func(g *tango.Group) {
g.Get("/1", func() string {
return "/1"
})
g.Post("/2", func() string {
return "/2"
})
}) Group也支持子Group:
t := tango.Classic()
t.Group("/api", func(g *tango.Group) {
g.Group("/v1", func(cg *tango.Group) {
cg.Get("/1", func() string {
return "/1"
})
cg.Post("/2", func() string {
return "/2"
})
})
}) 最后,Group也支持邏輯分組:
o := tango.Classic()
o.Group("", func(g *tango.Group) {
g.Get("/1", func() string {
return "/1"
})
})
o.Group("", func(g *tango.Group) {
g.Post("/2", func() string {
return "/2"
})
}) 這樣,即使路由間只是邏輯上分開,而并沒有上級路徑分開,也是可以分成不同的組。
返回值
通過前面的例子,我們會發現,所有執行體函數,可以有一個返回值或者沒有返回值。Tango的內部插件ReturnHandler來負責根據函數的第一個返回值的類型來自動的生成輸出,默認規則如下:
-
string返回string,則string會被轉換為bytes并寫入到ResponseWriter,默認狀態碼為200
-
[]byte返回[]byte, 則會直接寫入ResponseWriter,默認狀態碼為200
-
error返回error接口,如果不為nil, 則返回狀態碼為500,內容為error.Error()
-
AbortError返回tango.AbortError接口,如果不為nil,則返回狀態碼為AbortError.Code,內容為AbortError.Error()
當然了,你可以撰寫你自己的插件來判斷更多的返回值類型。返回值結合tango的一些tricker,可以極大的簡化代碼,比如:
如果Action結構體包含匿名結構體tango.Json或者tango.Xml,則返回值結果如下:
如果包含tango.Json匿名結構體,則返回頭的Content-Type會自動設置為:application/json:
-
如果返回值為error,則返回值為{“err”: err.Error()},狀態碼為200
-
如果返回值為AbortError,則返回值為{“err”: err.Error()},狀態碼為err.Code()
-
如果返回值為string,則返回值為{“content”: content},狀態碼為200
-
如果返回值為[]byte,則返回值為{“content”: string(content)},狀態碼為200
-
如果返回值為map,slice,結構體或其它可自動Json化的內容,則返回值為map自動json對應的值,狀態碼為200
例如:
type Action struct {
tango.Json
}
var i int
func (Action) Get() interface{} {
if i == 0 {
i = i + 1
return map[string]interface{}{"i":i}
}
return errors.New("could not visit")
}
func main() {
t := tango.Classic()
t.Any("/", new(Action))
t.Run()
} 以上例子,訪問時會始終返回json,第一次訪問會返回map,第二次返回error。(注:這里只是演示代碼,實際執行i必須加鎖)
- Compress
Tango擁有一個默認的壓縮中間件,可以按照擴展名來進行文件的壓縮。同時,你也可以要求某個Action自動或強制使用某種壓縮。比如:
type CompressExample struct {
tango.Compress // 添加這個匿名結構體,要求這個結構體的方法進行自動檢測壓縮
}
func (CompressExample) Get() string {
return fmt.Sprintf("This is a auto compress text")
}
o := tango.Classic()
o.Get("/", new(CompressExample))
o.Run() 以上代碼默認會檢測瀏覽器是否支持壓縮,如果支持,則看是否支持gzip,如果支持gzip,則使用gzip壓縮,如果支持deflate,則使用deflate壓縮。
type GZipExample struct {
tango.GZip // add this for ask compress to GZip, if accept-encoding has no gzip, then not compress
}
func (GZipExample) Get() string {
return fmt.Sprintf("This is a gzip compress text")
}
o := tango.Classic()
o.Get("/", new(GZipExample))
o.Run() 以上代碼默認會檢測瀏覽器是否支持gzip壓縮,如果支持gzip,則使用gzip壓縮,否則不壓縮。
type DeflateExample struct {
tango.Deflate // add this for ask compress to Deflate, if not support then not compress
}
func (DeflateExample) Get() string {
return fmt.Sprintf("This is a deflate compress text")
}
o := tango.Classic()
o.Get("/", new(DeflateExample))
o.Run() 以上代碼默認會檢測瀏覽器是否支持deflate壓縮,如果支持deflate,則使用deflate壓縮,否則不壓縮。
Static
Static 讓你用一行代碼可以完成一個靜態服務器。
func main() {
t := tango.New(Static("./public", "", []string{"index.html", "index.htm"}))
t.Run()
} 然后,將你的文件放到./public目錄下,你就可以通過瀏覽器放問到他們。比如:
http://localhost/images/logo.png --> ./public/images/logo.png
當然,你也可以加入你basicauth或者你自己的認證中間件,這樣就變為了一個私有的文件服務器。
func main() {
t := tango.New()
t.Use(AuthHandler)
t.Use(Static("./public", "", []string{"index.html", "index.htm"}))
t.Run()
} Handler
Handler 是tango的中間件。在tango中,幾乎所有的事情都由中間件來完成。撰寫一個你自己的中間件非常簡單,并且我們鼓勵您只加載需要的中間件。
tango的中間件只需要符合以下接口即可。
type Handler interface {
Handle(*tango.Context)
} 同時,tango也提供了tango.HandlerFunc,以方便你將一個函數包裝為中間件。比如:
func MyHandler() tango.HandlerFunc {
return func(ctx *tango.Context) {
fmt.Println("this is my first tango handler")
ctx.Next()
}
}
t := tango.Classic()
t.Use(MyHandler())
t.Run() 正常的形式也可以是:
type HelloHandler struct {}
func (HelloHandler) Handle(ctx *tango.Context) {
fmt.Println("before")
ctx.Next()
fmt.Println("after")
}
t := tango.Classic()
t.Use(new(HelloHandler))
t.Run() 當然,你可以直接將一個包含tango.Context指針的函數作為中間件,如:
tg.Use(func(ctx *tango.Context){
fmt.Println("before")
ctx.Next()
fmt.Println("after")
}) 為了和標準庫兼容,tango通過UseHandler支持http.Handler作為中間件,如:
tg.UseHandler(http.Handler(func(resp http.ResponseWriter, req *http.Request) {
})) 老的中間件會被action被匹配之前進行調用。
Call stack
以下是中間件的調用順序圖:
tango.ServeHttp
|--Handler1
|--Handler2
|-- ...HandlerN
|---Action(If matched)
...HandlerN--|
Handler2 ----|
Handler1--|
(end)--| 在中間件中,您的中間件代碼可以在Next()被調用之前或之后執行,Next表示執行下一個中間件或Action被執行(如果url匹配的話)。如果不調用Next,那么當前請求將會被立即停止,之后的所有代碼將不會被執行。
注入
更多的注入方式參見以下示例代碼:
Request
type Action struct {
tango.Req
} Response
type Action struct {
tango.Resp
} Context
type Action struct {
tango.Ctx
} Logger
type Action struct {
tango.Log
} Params
type Action struct {
tango.Params
} Json
type Action struct {
tango.Json
} Xml
type Action struct {
tango.Xml
} 第三方插件
目前已經有了一批第三方插件,更多的插件正在陸續開發中,歡迎大家進行貢獻:
- session - Session管理
- xsrf - 生成csrf token和進行校驗
- bind - 自動綁定參數到結構體
- renders - Go模板引擎插件
- dispatch - 多應用支持
- tpongo2 - Pongo2 模板引擎支持
- captcha - 驗證碼插件
- events - 事件插件
- flash - 在requests之間共享數據