## 路由
基于 [radix tree](http://en.wikipedia.org/wiki/Radix_tree) ,Echo 的路由查詢速度非常快。路由使用 [sync pool](https://golang.org/pkg/sync/#Pool) 來重用內存,實現無 GC 開銷下的零動態內存分配。
通過特定的 HTTP 方法,url 路徑和一個匹配的處理程序 (handler) 可以注冊一個路由。例如,下面的代碼則展示了一個注冊路由的例子:它包括 `Get` 的訪問方式, `/hello` 的訪問路徑,以及發送 `Hello World` HTTP 響應的處理程序。
```go
// 業務處理
func hello(c echo.Context) error {
return c.String(http.StatusOK, "Hello, World!")
}
// 路由
e.GET("/hello", hello)
```
你可以用 `Echo.Any(path string, h Handler)` 來為所有的 HTTP 方法發送注冊 處理程序(handler);如果僅需要為某個特定的方法注冊路由,可使用 `Echo.Match(methods []string, path string, h Handler)`。
Echo 通過 `func(echo.Context) error` 定義 handler 方法,其中 `echo.Context` 已經內嵌了 HTTP 請求和響應接口。
### 匹配所有
匹配零個或多個字符的路徑。例如, `/users/*` 將會匹配;
- `/users/`
- `/users/1`
- `/users/1/files/1`
- `/users/anything...`
### 路徑匹配順序
- Static (固定路徑)
- Param (參數路徑)
- Match any (匹配所有)
*實例*
```go
e.GET("/users/:id", func(c echo.Context) error {
return c.String(http.StatusOK, "/users/:id")
})
e.GET("/users/new", func(c echo.Context) error {
return c.String(http.StatusOK, "/users/new")
})
e.GET("/users/1/files/*", func(c echo.Context) error {
return c.String(http.StatusOK, "/users/1/files/*")
})
```
上面定義的路由將按下面的優先級順序匹配:
- `/users/new`
- `/users/:id`
- `/users/1/files/*`
> 路由可以按照任意順序定義。
### 組路由
`Echo#Group(prefix string, m ...Middleware) *Group`
可以將具有相同前綴的路由歸為一組從而定義具有可選中間件的新子路由。除了一些特殊的中間件外,組路由也會繼承父中間件。若要在組路由中添加中間件,則需使用 `Group.Use(m ...Middleware)` 。最后,組路由也可以嵌套。
下面的代碼,我們創建了一個 admin 組,它需要對 `/admin/*` 路由進行基本的 HTTP 身份認證。
```go
g := e.Group("/admin")
g.Use(middleware.BasicAuth(func(username, password string) bool {
if username == "joe" && password == "secret" {
return true
}
return false
}))
```
### 路由命名
每個路由都會返回一個 `Route` 對象,這個對象可以用來給路由命名。比如:
```go
routeInfo := e.GET("/users/:id", func(c echo.Context) error {
return c.String(http.StatusOK, "/users/:id")
})
routeInfo.Name = "user"
// 或者這樣寫
e.GET("/users/new", func(c echo.Context) error {
return c.String(http.StatusOK, "/users/new")
}).Name = "newuser"
```
當你需要在模版生成 uri 但是又無法獲取路由的引用,或者多個路由使用相同的處理器(handler)的時候,路由命名就會顯得更方便。
### 構造URI
`Echo#URI(handler HandlerFunc, params ...interface{})` 可以用來在任何業務處理代碼里生成帶有特殊參數的URI。這樣在你重構自己的應用程序的時候,可以很方便的集中處理所有的 URI 。
下面的代碼中 `e.URI(h, 1)` 將生成 `/users/1` :
```go
// 業務處理
h := func(c echo.Context) error {
return c.String(http.StatusOK, "OK")
}
// 路由
e.GET("/users/:id", h)
```
除了 `Echo#URI`,還可以使用 `Echo#Reverse(name string, params ...interface{})` 方法根據路由名生成 uri。比如,當 `foobar` 進行如下設置時,使用 `Echo#Reverse("foobar", 1234)` 就會生成 `/users/1234` :
```go
// Handler
h := func(c echo.Context) error {
return c.String(http.StatusOK, "OK")
}
// Route
e.GET("/users/:id", h).Name = "foobar"
```
### 路由列表
`Echo#Routes() []*Route` 會根據路由定義的順序列出所有已經注冊的路由。每一個路由包含 http 方法,路徑和對應的處理器(handler)。
*示例*
```go
// Handlers
func createUser(c echo.Context) error {
}
func findUser(c echo.Context) error {
}
func updateUser(c echo.Context) error {
}
func deleteUser(c echo.Context) error {
}
// Routes
e.POST("/users", createUser)
e.GET("/users", findUser)
e.PUT("/users", updateUser)
e.DELETE("/users", deleteUser)
```
用下面的代碼你將所有的路由信息輸出到 JSON 文件:
```go
data, err := json.MarshalIndent(e.Routes(), "", " ")
if err != nil {
return err
}
ioutil.WriteFile("routes.json", data, 0644)
```
`routes.json`
```json
[
{
"method": "POST",
"path": "/users",
"handler": "main.createUser"
},
{
"method": "GET",
"path": "/users",
"handler": "main.findUser"
},
{
"method": "PUT",
"path": "/users",
"handler": "main.updateUser"
},
{
"method": "DELETE",
"path": "/users",
"handler": "main.deleteUser"
}
]
```