go-tango微內核 Go 語言 Web 框架
Tango 是微內核可擴展的Go語言Web框架。
Golang的web框架基本上處于一個井噴期,那么為什么要再造一個輪子。這是因為,目前可擴展性比較強的都是基于函數(shù)作為可執(zhí)行體的,而以結構體作為執(zhí)行體的框架目前可擴展性都不夠強,包括我原先寫的框架xweb也是如此。因此,一個全新的框架出來了,先上地址:https://github.com/lunny/tango。
初看Tango框架,感覺和Martini及Macaron非常的像。比如這段代碼:
package main
import "github.com/lunny/tango"
func main() {
t := tango.Classic()
t.Get("/", func() string {
return "Hello tango!"
})
t.Run()
}
這種其實大家都支持的。再看這個吧:
package main
import "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()
}
Tango同時支持函數(shù)和結構體作為執(zhí)行體,不過主打是結構體,函數(shù)只作為兼容而用。下面對Tango的各種功能一一道來。
路由
Tango目前同時支持3種路由規(guī)則:
靜態(tài)路由
tg.Get("/", new(Action))
命名路由
tg.Get("/:name", new(Action))
命名路由對應的參數(shù)內容可通過ctx.Params().Get(":name")來獲得
正則路由
tg.Get("/(.*)", new(Action))
正則路由對應的參數(shù)內容可通過ctx.Params().Get(":0")來獲得
這里要注意命名路由和正則路由不可混用。
執(zhí)行體
同時支持函數(shù)執(zhí)行體和結構體執(zhí)行體,支持的函數(shù)執(zhí)行體形式如下,可以有零個或一個返回值,返回值由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作為成員方法的結構體作為執(zhí)行體。
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"
})
})
這樣,即使路由間只是邏輯上分開,而并沒有上級路徑分開,也是可以分成不同的組。
返回值
通過前面的例子,我們會發(fā)現(xiàn),所有執(zhí)行體函數(shù),可以有一個返回值或者沒有返回值。Tango的內部插件ReturnHandler來負責根據(jù)函數(shù)的第一個返回值的類型來自動的生成輸出,默認規(guī)則如下:
string 返回string,則string會被轉換為bytes并寫入到ResponseWriter,默認狀態(tài)碼為200
[]byte 返回[]byte, 則會直接寫入ResponseWriter,默認狀態(tài)碼為200
error 返回error接口,如果不為nil, 則返回狀態(tài)碼為500,內容為error.Error()
AbortError 返回tango.AbortError接口,如果不為nil,則返回狀態(tài)碼為AbortError.Code,內容為AbortError.Error()
當然了,你可以撰寫你自己的插件來判斷更多的返回值類型。返回值結合tango的一些tricker,可以極大的簡化代碼,比如:
如果Action結構體包含匿名結構體tango.Json或者tango.Xml,則返回值結果如下:
如果包含tango.Json匿名結構體,則返回頭的Content-Type會自動設置為:application/json:
如果返回值為error,則返回值為{“err”: err.Error()},狀態(tài)碼為200
如果返回值為AbortError,則返回值為{“err”: err.Error()},狀態(tài)碼為err.Code()
如果返回值為string,則返回值為{“content”: content},狀態(tài)碼為200
如果返回值為[]byte,則返回值為{“content”: string(content)},狀態(tài)碼為200
如果返回值為map,slice,結構體或其它可自動Json化的內容,則返回值為map自動json對應的值,狀態(tài)碼為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。(注:這里只是演示代碼,實際執(zhí)行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 讓你用一行代碼可以完成一個靜態(tài)服務器。
func main() {
t := tango.New(tango.Static())
t.Run()
}
然后,將你的文件放到 ./public 目錄下,你就可以通過瀏覽器放問到他們。比如:
http://localhost/images/logo.png --> ./public/images/logo.png
當然,你也可以加入你basicauth或者你自己的認證中間件,這樣就變?yōu)榱艘粋€私有的文件服務器。
func main() {
t := tango.New()
t.Use(AuthHandler)
t.Use(tango.Static())
t.Run()
}
Handler
Handler 是tango的中間件。在tango中,幾乎所有的事情都由中間件來完成。撰寫一個你自己的中間件非常簡單,并且我們鼓勵您只加載需要的中間件。
tango的中間件只需要符合以下接口即可。
type Handler interface {
Handle(*tango.Context)
}
同時,tango也提供了tango.HandlerFunc,以方便你將一個函數(shù)包裝為中間件。比如:
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指針的函數(shù)作為中間件,如:
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()被調用之前或之后執(zhí)行,Next表示執(zhí)行下一個中間件或Action被執(zhí)行(如果url匹配的話)。如果不調用Next,那么當前請求將會被立即停止,之后的所有代碼將不會被執(zhí)行。
注入
更多的注入方式參見以下示例代碼:
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
}
第三方插件
目前已經(jīng)有了一批第三方插件,更多的插件正在陸續(xù)開發(fā)中,歡迎大家進行貢獻:
session - Session管理
xsrf - 生成csrf token和進行校驗
bind - 自動綁定參數(shù)到結構體
renders - Go模板引擎插件
dispatch - 多應用支持
captcha - 驗證碼插件
events - 事件插件
flash - 在requests之間共享數(shù)據(jù)
