## 处理程序类型
处理程序, 顾名思义,处理请求。
一个处理程序响应一个 HTTP 请求。它将回复标头和数据写入 `Context.ResponseWriter()`函数,然后返回它们。返回信号表明请求已完成; 在 `Handler` 调用完成之后或与之同时使用 `Context` 是无效的。
根据 HTTP 客户端软件,HTTP 协议版本,及客户端和 iris 服务之间的任何中介结构, 可能无法在写入 `Context.Request().Body` 之后从 `Context.ResponseWriter()` 函数中读取内容。谨慎的处理程序,应首先读取 `Context.Request().Body`,然后进行答复它们。
除了读取正文,处理程序不应修改提供的 `Context`。
如果 `Handler` 出现紧急情况,则服务器(`Handler`的调用方)将假定紧急情况的影响与活动请求无关。它恢复了紧急状态,将堆栈跟踪记录到服务器错误日志中并中断了连接。
```
type Handler func(iris.Context)
```
注册处理程序后,我们可以使用返回的 [`路由`](https://godoc.org/github.com/kataras/iris/core/router#Route) 实例为该实例命名处理程序注册,以便于调试或匹配视图中的相对路径。 有关更多信息,请查看 [反向查询](https://github.com/kataras/iris/wiki/Routing-reverse-lookups) 部分。
## [行为](https://github.com/kataras/iris/wiki/Routing#behavior)
Iris 的默认行为是接受和注册像 `/api/user` 这类路径的路由,而且路径末端不带斜杠。如果客户端尝试访问 `$your_host/api/user/` ,则 Iris 路由器将自动将其重定向到 `$your_host/api/user`,以便由注册路由处理它。这是设计 API 的现代方法。
但是,假如你想要对请求的资源 **禁用路径校正**, 你可以传递 iris [配置](https://github.com/kataras/iris/wiki/Configuration)的 `iris.WithoutPathCorrection` 选项到 `app.Run` 函数。 例如:
```
// [app := iris.New...]
// [...]
app.Run(iris.Addr(":8080"), iris.WithoutPathCorrection)
```
如果想为`/api/user` 和 `/api/user/`路径,保持相同的处理程序和路由**而无需重定向**(常见情况) ,那么仅仅使用 `iris.WithoutPathCorrectionRedirection` 选项即可:
```source-go
app.Run(iris.Addr(":8080"), iris.WithoutPathCorrectionRedirection)
```
## [API](https://github.com/kataras/iris/wiki/Routing#api)
所有的 HTTP 方法都是被支持的,开发者也可以在相同的路径上注册不同的方法。
第一个参数是 HTTP 方法,第二个参数是路由的请求路径,第三个可变参数应包含一个或多个 `iris.Handler`,当客户端从客户端请求该特定资源路径时,这些 `iris.Handler` 将按照注册顺序执行。
示例代码:
```
app := iris.New()
app.Handle("GET", "/contact", func(ctx iris.Context) {
ctx.HTML("<h1> Hello from /contact </h1>")
})
```
为了方便开发者, iris 为所有 HTTP 方法提供了辅助方法。 第一个参数是路由的请求路径,第二个参数应包含一个或多个 `iris.Handler`,当用户从服务器请求该特定资源路径时,这些 `iris.Handler` 将会按照注册顺序执行。
示例代码:
```
app := iris.New()
// 方法: "GET"
app.Get("/", handler)
// 方法: "POST"
app.Post("/", handler)
// 方法: "PUT"
app.Put("/", handler)
// 方法: "DELETE"
app.Delete("/", handler)
// 方法: "OPTIONS"
app.Options("/", handler)
// 方法: "TRACE"
app.Trace("/", handler)
// 方法: "CONNECT"
app.Connect("/", handler)
// 方法: "HEAD"
app.Head("/", handler)
// 方法: "PATCH"
app.Patch("/", handler)
// 注册支持所有 HTTP 方法的路由
app.Any("/", handler)
func handler(ctx iris.Context){
ctx.Writef("Hello from method: %s and path: %s\n", ctx.Method(), ctx.Path())
}
```
### [离线路线](https://github.com/kataras/iris/wiki/Routing#offline-routes)
在 Iris 中有一种特别的方法。 它被称为 `None` 并且可以用它对外部隐藏一个路由, 但是你却可以通过 `Context.Exec` 方法从其他路由处理程序中调用它。每个 API 处理方法都会返回路由的 值。路由的 `IsOnline` 方法报告该路由的当前状态。你可以通过路由 `Route.Method` 字段的值,将路由的**状态**从**离线**改变**在线**。 当然,在服务时路由器的每次更改都需要一个`app.RefreshRouter()` 调用,该调用可以安全使用。下面看一个更完整的示例:
```
// 文件: main.go
package main
import (
"github.com/kataras/iris/v12"
)
func main() {
app := iris.New()
none := app.None("/invisible/{username}", func(ctx iris.Context) {
ctx.Writef("Hello %s with method: %s", ctx.Params().Get("username"), ctx.Method())
if from := ctx.Values().GetString("from"); from != "" {
ctx.Writef("\nI see that you're coming from %s", from)
}
})
app.Get("/change", func(ctx iris.Context) {
if none.IsOnline() {
none.Method = iris.MethodNone
} else {
none.Method = iris.MethodGet
}
//刷新服务中重建的路由器,以便
// 收到新路线通知。
app.RefreshRouter()
})
app.Get("/execute", func(ctx iris.Context) {
if !none.IsOnline() {
ctx.Values().Set("from", "/execute with offline access")
ctx.Exec("NONE", "/invisible/iris")
return
}
// 与导航到 "http://localhost:8080/invisible/iris" 相同
// 当 /change 被调用并且路由状态从
// "离线" 改变为 "在线"
ctx.Values().Set("from", "/execute")
// 值和 session 可以被共享,
// 当调用 Exec 从一个"外部"的 Context。
// ctx.Exec("NONE", "/invisible/iris")
// 或者在 "/change" 之后:
ctx.Exec("GET", "/invisible/iris")
})
app.Run(iris.Addr(":8080"))
}
```
**如何运行**
1. `go run main.go`
2. 打开一个位于 `http://localhost:8080/invisible/iris` 的浏览器, 然后你会获得一个 `404 not found` 错误,
3. 但是 `http://localhost:8080/execute` 将能够执行该路由。
4. 现在,如果导航到 `http://localhost:8080/change` 并刷新 `/invisible/iris`选项卡 ,你将会看到它。
## [路由分组](https://github.com/kataras/iris/wiki/Routing#grouping-routes)
被路径前缀分组的一组路由可以(可选)共享相同的中间件处理程序和模板布局。 一个组同时也可以有一个嵌套组。
`.Party` 用于对路由进行分组,开发人员可以声明无限数量的(嵌套)组。
示例代码:
```
app := iris.New()
users := app.Party("/users", myAuthMiddlewareHandler)
// http://localhost:8080/users/42/profile
users.Get("/{id:uint64}/profile", userProfileHandler)
// http://localhost:8080/users/messages/1
users.Get("/messages/{id:uint64}", userMessageHandler)
```
也可以使用 `PartyFunc` 函数编写相同的方法,该方法接受子路由器(当前分组的子路由器)。
```source-go
app := iris.New()
app.PartyFunc("/users", func(users iris.Party) {
users.Use(myAuthMiddlewareHandler)
// http://localhost:8080/users/42/profile
users.Get("/{id:uint64}/profile", userProfileHandler)
// http://localhost:8080/users/messages/1
users.Get("/messages/{id:uint64}", userMessageHandler)
})
```
## [路径参数](https://github.com/kataras/iris/wiki/Routing#path-parameters)
和你以前见到的路由器不同,Iris 的路由器可以处理不同种类的路由而不会发生冲突。
仅仅匹配 GET "/" 路径。
```
app.Get("/", indexHandler)
```
匹配所有包含 `"/assets/**/*"` 前缀的 GET 请求, 它是一个 `ctx.Params().Get("asset")` 的通配符,等同于任何跟随在 `/assets/`之后的路径。
```
app.Get("/assets/{asset:path}", assetsWildcardHandler)
```
匹配所有以 `"/profile/"` 为前缀的 GET 请求,后跟随单个路径部分。
```
app.Get("/profile/{username:string}", userHandler)
```
1.
仅匹配 `"/profile/me"` GET 请求并且不与 `/profile/{username:string}` 请求或者任何root 通配符 `/{root:path}` 请求相冲突。
```
app.Get("/profile/me", userHandler)
```
匹配所有以 `/users/` 为前缀的GET请求,后跟一个等于或大于 1 的数字。
```
app.Get("/user/{userid:int min(1)}", getUserHandler)
```
匹配所有以 `/ users /`为前缀的 DELETE 请求,后跟一个等于或大于 1 的数字。
```
app.Delete("/user/{userid:int min(1)}", deleteUserHandler)
```
匹配除其他路由已处理的请求之外的所有 GET 请求。例如,在这个案例中,前面已经注册的路由; `/`, `/assets/{asset:path}`,`/profile/{username}`, `"/profile/me"`,`/user/{userid:int ...}`。它与其余路由(!)不冲突。
```
app.Get("{root:path}", rootWildcardHandler)
```
匹配以下所有 GET 请求:
1. /u/abcd 映射到 `:alphabetical` (如果 `:alphabetical` 已经被注册,则使用 `:string`)
2. /u/42 映射到 `:uint` (if `:uint` 已经被注册,则使用 `:int`)
3. /u/-1 映射到 `:int` (if `:int` 已经被注册,则使用 `:string`)
4. /u/abcd123 映射到 `:string`
```
app.Get("/u/{username:string}", func(ctx iris.Context) {
ctx.Writef("username (string): %s", ctx.Params().Get("username"))
})
app.Get("/u/{id:int}", func(ctx iris.Context) {
ctx.Writef("id (int): %d", ctx.Params().GetIntDefault("id", 0))
})
app.Get("/u/{uid:uint}", func(ctx iris.Context) {
ctx.Writef("uid (uint): %d", ctx.Params().GetUintDefault("uid", 0))
})
app.Get("/u/{firstname:alphabetical}", func(ctx iris.Context) {
ctx.Writef("firstname (alphabetical): %s", ctx.Params().Get("firstname"))
})
```
分别匹配 `/abctenchars.xml` 和 `/abcdtenchars` 的所有 GET 请求。
```
app.Get("/{alias:string regexp(^[a-z0-9]{1,10}\\.xml$)}", PanoXML)
app.Get("/{alias:string regexp(^[a-z0-9]{1,10}$)}", Tour)
```
你可能想知道 `{id:uint64}` 或者 `:path` 或者 `min(1)` 是什么意思。 它们是(键入的)动态路径参数,可以在其上注册功能。通过阅读 [路由参数类型](https://github.com/kataras/iris/wiki/Routing-path-parameter-types) 了解更多信息。
