# 路由
## 介紹
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 的編程思想,“簡單,靈活”; 如果業務模塊龐大,根據不同文件不同包來區分各類路由組也都是可以的,取決于你對自己業務的把控。