Tango 是微内核可扩展的Go语言Web框架。
Golang的web框架基本上处于一个井喷期,那么为什么要再造一个轮子。这是因为,目前可扩展性比较强的都是基于函数作为可执行体的,而以结构体作为执行体的框架目前可扩展性都不够强,包括我原先写的框架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同时支持函数和结构体作为执行体,不过主打是结构体,函数只作为兼容而用。下面对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 让你用一行代码可以完成一个静态服务器。
func main() { t := tango.New(tango.Static()) t.Run() }
然后,将你的文件放到 ./public 目录下,你就可以通过浏览器放问到他们。比如:
http://localhost/images/logo.png --> ./public/images/logo.png
当然,你也可以加入你basicauth或者你自己的认证中间件,这样就变为了一个私有的文件服务器。
func main() { t := tango.New() t.Use(AuthHandler) t.Use(tango.Static()) t.Run() }
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被匹配之前进行调用。
以下是中间件的调用顺序图:
tango.ServeHttp |--Handler1 |--Handler2 |-- ...HandlerN |---Action(If matched) ...HandlerN--| Handler2 ----| Handler1--| (end)--|
在中间件中,您的中间件代码可以在Next()被调用之前或之后执行,Next表示执行下一个中间件或Action被执行(如果url匹配的话)。如果不调用Next,那么当前请求将会被立即停止,之后的所有代码将不会被执行。
更多的注入方式参见以下示例代码:
type Action struct { tango.Req }
type Action struct { tango.Resp }
type Action struct { tango.Ctx }
type Action struct { tango.Log }
type Action struct { tango.Params }
type Action struct { tango.Json }
type Action struct { tango.Xml }
目前已经有了一批第三方插件,更多的插件正在陆续开发中,欢迎大家进行贡献:
session - Session管理
xsrf - 生成csrf token和进行校验
bind - 自动绑定参数到结构体
renders - Go模板引擎插件
dispatch - 多应用支持
captcha - 验证码插件
events - 事件插件
flash - 在requests之间共享数据
If you device showed "tango service outdated." It means that your Tango Core need to be updated. Please go to Play Store, Search for "Tango Core" and updated it. Just making sure your Tango Core is up
目录 监控系统 容器技术 PaaS工具 大数据 微服务 CI/CD 数据库技术 存储技术 分布式系统 消息系统 服务器管理 安全工具 网络工具 Web工具 Web框架 区块链技术 其它 监控系统 项目 简介 OpenFalcon OpenFalcon是一款小米开源的监控系统。功能:数据采集免配置:agent自发现、支持Plugin、主动推送模式; 容量水平扩展:生产环境每秒50万次数据收集、告警、
Go语言具有支持高并发的特性,可以很方便地实现多线程运算,充分利用多核心 cpu 的性能。 众所周知服务器的处理器大都是单核频率较低而核心数较多,对于支持高并发的程序语言,可以充分利用服务器的多核优势,从而降低单核压力,减少性能浪费。 Go语言实现多核多线程并发运行是非常方便的,下面举个例子: 运行结果如下: 线程0, sum为:10000 线程2, sum为:10000 线程3, sum为:10
包(package)是多个 Go 源码的集合,是一种高级的代码复用方案,像 fmt、os、io 等这样具有常用功能的内置包在 Go语言中有 150 个以上,它们被称为标准库,大部分(一些底层的除外)内置于 Go 本身。 包要求在同一个目录下的所有文件的第一行添加如下代码,以标记该文件归属的包: package 包名 包的特性如下: 一个目录下的同级文件归属一个包。 包名可以与其目录不同名。 包名为
主要内容:使用命令行查看GOPATH信息,使用GOPATH的工程结构,设置和使用GOPATH,在多项目工程中使用GOPATHGOPATH 是 Go语言中使用的一个环境变量,它使用绝对路径提供项目的工作目录。 工作目录是一个工程开发的相对参考目录,好比当你要在公司编写一套服务器代码,你的工位所包含的桌面、计算机及椅子就是你的工作区。工作区的概念与工作目录的概念也是类似的。如果不使用工作目录的概念,在多人开发时,每个人有一套自己的目录结构,读取配置文件的位置不统一,输出的二进制运行文件也不统一,这样
Go语言中 continue 语句可以结束当前循环,开始下一次的循环迭代过程,仅限在 for 循环内使用,在 continue 语句后添加标签时,表示开始标签对应的循环,例如: 代码输出结果如下: 0 2 1 2 代码说明:第 14 行将结束当前循环,开启下一次的外层循环,而不是第 10 行的循环。
Go语言中的 map 在并发情况下,只读是线程安全的,同时读写是线程不安全的。 下面来看下并发情况下读写 map 时会出现的问题,代码如下: 运行代码会报错,输出如下: fatal error: concurrent map read and map write 错误信息显示,并发的 map 读和 map 写,也就是说使用了两个并发函数不断地对 map 进行读和写而发生了竞态问题,map 内部会对
标准的Go语言代码库中包含了大量的包,并且在安装 Go 的时候多数会自动安装到系统中。我们可以在 $GOROOT/src/pkg 目录中查看这些包。下面简单介绍一些我们开发中常用的包。 1) fmt fmt 包实现了格式化的标准输入输出,这与C语言中的 printf 和 scanf 类似。其中的 fmt.Printf() 和 fmt.Println() 是开发者使用最为频繁的函数。 格式化短语派生