# 路由
## 介绍
Orange 框架中的路由接管了 http 包中的路由查找,提供了路由群组,表达式路由等多种策略,提高了灵活性;存储模型参考了 Echo 框架,采用了类似 Radix tree 的多路查找树模型,有较好的路由查找效率,尤其在有大量表达式路由时相比正则匹配优势比较明显;同时也能很好的解决哈希存储的哈希冲突问题。
## 使用说明
路由文件默认在 route/http.go 中声明。
路由定义需要通过结构体实现 `app.AppSrv` 接口,即接口体中需要有 `func (s *Route) ServeMux() ` 和 `func (s *Route) Register() ` 方法。
ServeMux 方法用户定义路由信息,Register方法用于初始化启动自定义服务,和 init 方法的区别是,Register方法在框架相关组件加载完成后再执行,避免了一些空指针的情况。
## 定义路由组
通过路由组可以定义有相同前缀url和执行相同中间件的一类路由;
通过 `groupName := app.GroupRouter("/api", NewMiddleWare1(), NewMiddleWare2)` 来实现;
## 路由定义
* 定义GET请求 `groupName .GET("/upload", controller.Upload)`
* 定义POST请求 `groupName .POST("/upload", controller.Upload)`
* 定义接受所有请求 `groupName .ALL("/upload", controller.Upload)`
## 自定义 http handler 路由输入
用户可以自行创建原生的 http hander,然后将对应的 hander 注入到框架中,按如下方式实现:
```
type MyHandler struct {
}
func (MyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
w.Write([]byte("custom http handler"))
}
// 注入自定义http handler 不走框架中间件,直接调用
app.NewHttpHandler("/custom", MyHandler{})
```
### 静态路由
静态路由是定义一个固定的访问地址,需url和路由信息完全匹配;
如:`groupName .GET("/upload", controller.Upload)`,访问时 url 必须是 /upload;
### 表达式-参数路由
参数路由会将定义的参数设置成变量,匹配到相应格式即可;
如:`groupName .GET("/userinfo/:uid", controller.UserInfo)` ,访问时 url 是 /userinfo/1,/userinfo/2, /userinfo/199 等等;
### 表达式-全匹配路由
类似与模糊匹配,url 只需要前缀一致即可匹配;
如:`groupName .GET("/userhome/*", controller.UserHome)`
访问时 url 是 /userhome/001,/userhome/001/find, /userinfo/get/101 等等,后面多个斜线也都能匹配上;
### 匹配优先级
在多种路由同时定义时,可能会出现模糊匹配覆盖其他类型的情况,我们定义了一套符合常理的优先级;
静态路由 > 参数路由 > 全匹配路由
## 路由注入
在项目入口文件中,将路由结构体传入app启动方法,如下:
```
func main() {
router := &http.Route{}
app.AppStart(router)
}
```
## 完整示例
```
package http
import (
"gitee.com/zhucheer/orange/app"
"gitee.com/zhucheer/orange/project/http/controller"
"gitee.com/zhucheer/orange/project/http/middleware"
)
type Route struct {
}
func (s *Route) ServeMux() {
commonGp := app.GroupRouter("") // 定义一个路由组 commonGp
{
commonGp.GET("/", func(ctx *app.Context) error { // commonGp 路由组下的一个 GET 请求类型路由 /
return ctx.ToJson(map[string]interface{}{"data": "orange framework"})
})
commonGp.ALL("/upload", controller.Upload) // commonGp 路由组下的一个所有请求类型路由 /upload
}
// 定义路由组 authGp 该路由组会执行一个中间件 Auth
authGp := app.GroupRouter("/auth", middleware.NewAuth())
{
authGp.ALL("/info", controller.AuthCheck) // 定义 authGp 路由组下所有请求类型路由 /auth/info
}
}
func (s *Route) Register() {
}
```
## 其他说明
在上面完整示例中用到了花括号的另一个用法,有些同学对这里会有点疑惑,我在这里解释解释;
在 Golang 中 `{ }` 花括号的用法除了和其他语言通用的包裹函数体之外还有另外一个用途,那就是可以通过花括号来区分代码块,将业务相对独立的代码区域进行分割,上述路由示例中就是这种用法;因此它也没有不能另起一行定义的限制;这里需要大家理解;
**值得注意的是**,代码块中申明的变量作用域仅在块中生效,这样可以将不同的路由群组进行有效的区分,提高代码的可读性,减少因变量泛滥引起的问题;
有同学可能会问,"为什么不封装成函数?" 当然,封装成函数固然也是一种方法,这也正是 Golang 的编程思想,“简单,灵活”; 如果业务模块庞大,根据不同文件不同包来区分各类路由组也都是可以的,取决于你对自己业务的把控。