Aurora Web 使用示例
其实在使用的方式上和大部分 go web 框架相差不大
简单的写一个入门demo:
package main
import (
"github.com/aurora-go/aurora/aurora"
)
func main() {
//获取 aurora 路由实例
a := aurora.New()
// GET 方法注册 web get请求
a.GET("/", func(request aurora.Request) interface{} {
return nil
})
// 启动服务器 默认端口8080,更改端口号 a.Guide(”8081“) 即可
a.Guide("")
}
复制代码
废话就不多说了,下面对Aurora Web做一个简单的介绍
路由设计
起初在没有参考优秀的web框架时,也曾多次尝试对路由存储的设计,但并没有想到更好的方式来灵活处理,通过gin和httprouter的源码借鉴,也作出了选择。采用前缀树的方式来对路由进行切割存储(切割存储存在一些弊端,同时也成为了 aurora 的短板之处)
先贴出router 核心代码以便分析:
// ServerRouter Aurora核心路由器
type route struct {
mx *sync.Mutex
tree map[string]*node // 路由树根节点
defaultView Views // 默认视图处理器,初始化采用 Aurora 实现的函数进行渲染
ar *Aurora // Aurora 引用
}
// Node 路由节点
type node struct {
Path string //当前节点路径
handle []Handel //服务处理函数
Child []*node //子节点
}
复制代码
下面的规则是在开发过程中累计起来的,有需要的伙伴可以参考一下
路由器规则:
- 无法存储相同的路径,形同路径的判定:校验参数相同,并且节点函数不为nil,节点函数为nil的节点说明,这个路径是未注册过,被提取为公共根。注册相同路径处理函数,默认覆盖前面相同的处理函数。
- 路径查找按照逐层检索
- 路由树上面存储者当前路径匹配的服务处理函数
- 注册路径必须以 / 开头
- 发生公共根。节点和被添加路径产生公共根,提取公共根后,若公共根未注册,服务处理函数将为ni,若节点恰好是公共根,则设置函数
- REST 风格注册。同一个根路径下只能有一个REST 子路径,REST 作为根路径也只能拥有一个REST 子路径,REST 路径会和其它非REST同级路径发生冲突。
- 注册路径不能以/结尾(bug未修复,/user /user/ 产生 /user 的公共根 使用切割解析路径方式,解析子路径,拼接剩余子路径会存在bug ,注册路径的时候强制无法注册 / 结尾的 url)
- 浏览器访问接口,不能带有可编码符号,特别是{} ,{}是框架解析rest ful 参数的标识,接收到带有{},比如/a/b/{sss}/c,带有{或}的请求都视为非法url
现在我有以下的Web接口开始注册:
package main
import (
"github.com/aurora-go/aurora/aurora"
)
func Default(r aurora.Request) interface{} {
return "nil"
}
func main() {
//获取 aurora 路由实例
a := aurora.New()
a.GET("/aaa/bbb", Default)
a.GET("/aa", Default)
a.GET("/", Default)
a.GET("/bbb", Default)
a.GET("/a/c", Default)
a.GET("/ccc", Default)
a.GET("/ccc/ddd", Default)
a.GET("/ccc/ddd/{a}/{b}", Default)
a.GET("/eee/aaa", Default)
a.GET("/eee/aaa/oo", Default)
a.GET("/eee/aaa/oo/{ccc}", Default)
a.GET("/eee/aaa/oo/{ccc}/abc", Default)
// 启动服务器 默认端口8080,更改端口号 a.Guide(”8081“) 即可
a.Guide("")
}
复制代码
上面的接口大致会呈现一下的数据结构
接口比较多,选了开始的一小部分表示出来
在前缀树中我们能找到几条完整路径:
- /
- /aa
- /a
- /a/c
- /aaa/bbb
- /ccc/ddd/{a}/{b}
- /eee/aaa
在前缀书上,我们可以走出以上的路径,每走一步进行一个拼接能得到一个完整的路径,在一个请求达到之后,开始在路由树上面一步一步的走,没有找到则算改请求是无效的。
现在我们知道了路由原理,从图中和前缀树的构造规则来看,子路径越多,被切割的层数也就越深。而且在构造路由书的中间存在对公共部分的提取,有些节点不一定是一个接口。
生命周期
初始化阶段
初始化图从上向下看
请求处理
一个浏览器的请求周期如下图所示,经过路由判断类型,静态资源(html, 图片,js等)无须查找接口直接响应,根据接口的不同和预处理配置的不同执行到处理函数,然后做出响应。
静态资源
关于静态资源的实现细节可以查阅源码,在此处简单解说一下。更具go web的原生处理方式,想要发送资源只能通过写入数据,静态资源的查找是核心关键,读取服务器上指定路径的资源,添加上对应的头类型即可完成资源的访问,浏览器就能够正确的解读资源并展现出来。aurora 的静态资源处理做了最简单的封装,其默认情况下项目根目录就是存放处,当然也提供了指定资源位置的操作。
预处理机制
预处理机制,就是对Web 业务逻辑的执行之前进行抢先执行,Go 用户常称作中间件,Java 用户常叫做拦截器或者过滤器。我是从Java web 转到 Go Web ,各自的叫法也都习惯了,起初对预处理的设计模仿了Java 的拦截器处理方式,但是用在Go上面能实现效果,但是性能确是不怎么样,也可能是经验不足没有设计好。还是得站在巨人的肩膀上做设计更加便捷,我采取和gin类似的做法,根据具全局和局部,按顺序执行下去即可。
参数解析
aurora 的参数解析,选择了map[string]interfac{} 类型 ,把所有类型的请求参数全部解析到 map 中更具实际情况直接取值使用即可,同时map中初始化内置了 go web 的原生请求体和响应体,提供开发者使用。
响应处理
个人对于gin的处理方式,其实有一点不太习惯的,gin的处理函数没有返回值,这个机制让我在某个逻辑条件下处理完响应或者处理完错误时候 没有下意识的 return 结束执行,导致经常报错。借着这个机会我给aurora的处理函数添加了返回值 interface{} 用于处理多种类型的数据,此时设计了返回值处理,也对我的预处理执行链提供了意想不到的 便利。
代码仓库
最后附上框架github 库 Aurora Web,相关使用文档。该框架是在学习go开发期间零碎时间一点一点起来的,至今也有大半年时间了,我还会继续维护下去,有意思的小伙伴可以尝试一下使用体验感,能有助于我修改和维护没有发现的bug?