进阶 www.iris-go.com 2026-10-20 02:39:14 · 20 阅读
第21章 Iris 框架中的 MVC 架构模式
MVC 使用 Iris MVC 实现代码复用。通过创建相互独立的组件,开发者可以方便快捷地将组件复用到其他应用中。同一个应用中的视图(或相似视图)可以重构用于另一个使用不同数据的应用,因为视图只负责处理数据如何展示给用户。
Iris 原生支持 MVC(Model View Controller)架构模式,在 Go 生态中别处找不到这样的功能。你需要导入 iris/mvc 子包。
goimport "github.com/kataras/iris/v12/mvc"
Iris Web 框架支持请求数据、模型、持久化数据以及绑定,执行效率最高。
如果你是 Web 后端开发新手,建议先阅读关于 MVC 架构模式的内容,可以从维基百科的文章开始。
注意:继续之前请先阅读依赖注入部分。
特性
支持所有 HTTP 方法。例如,若要处理 GET 请求,控制器应包含名为 Get() 的函数;POST 使用 Post();带参数时使用 By 关键字,如 PostUserBy(id uint64),对应 POST: [...]/User/{id}。
可以通过 BeforeActivation 自定义事件回调,按控制器将自定义结构的 controller 方法作为 handler 提供服务,支持自定义路径(甚至包含正则参数的路径)。示例:
goimport (
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/mvc"
)
func main() {
app := iris.New()
mvc.Configure(app.Party("/root"), myMVC)
app.Listen(":8080")
}
func myMVC(app *mvc.Application) {
// app.Register(...)
// app.Router.Use/UseGlobal/Done(...)
app.Handle(new(MyController))
}
type MyController struct {}
func (m *MyController) BeforeActivation(b mvc.BeforeActivation) {
// b.Dependencies().Add/Remove
// b.Router().Use/UseGlobal/Done
// 以及任何你熟悉的标准 Router API 调用
// 1-> Method
// 2-> Path
// 3-> 要解析为 handler 的控制器函数名
// 4-> 在 MyCustomHandler 之前执行的中件
b.Handle("GET", "/something/{id:int64}", "MyCustomHandler", anyMiddleware...)
}
// GET: http://localhost:8080/root
func (m *MyController) Get() string {
return "Hey"
}
// GET: http://localhost:8080/root/something/{id:int64}
func (m *MyController) MyCustomHandler(id int64) string {
return "MyCustomHandler says Hey"
}
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
在 Controller 结构体中定义服务并注入到 Dependencies 中,或将 Controller 的作用域设置为 Singleton,即可在请求之间共享持久化数据。控制器之间可以共享依赖项,也可以将它们注册到父级 MVC 应用中。此外,你还能在 Controller 内部的可选 BeforeActivation 事件回调中,针对特定控制器修改依赖项。例如:`func(c *MyController) BeforeActivation(b mvc.BeforeActivation) { b.Dependencies().Add/Remove(...) }`。 你可以将 Context 作为控制器的字段直接访问(无需手动绑定),例如 `Ctx iris.Context`;也可以将其作为方法入参传递,例如 `func(ctx iris.Context, otherArguments...)`。 你可以通过 Method 函数设置 Controller 结构体中的 Models,并由 View 进行渲染。你可以从控制器的方法中返回模型,或在请求生命周期中设置某个字段,并在同一生命周期内的其他方法中返回该字段。 路由机制与以往一致,MVC 应用拥有自己的 Router,它是 iris/router.Party 类型,遵循标准 iris API。控制器可以注册到任意 Party(包括 Subdomains),Party 的 begin 和 done 处理程序也能按预期工作。 可选的 `BeginRequest(ctx)` 函数用于在方法执行前进行初始化,适合调用中间件或在多个方法使用相同数据集合的场景下做预处理。 可选的 `EndRequest(ctx)` 函数用于在任何方法执行后做收尾工作。 支持递归继承。例如我们的 mvc 会话控制器示例中,将 `Session *sessions.Session` 作为结构体字段,由会话管理器负责填充。 通过控制器方法的入参可直接访问动态路径参数,无需绑定。当使用 Iris 默认语法从控制器解析处理函数时,方法名需以 By 为后缀,大写单词代表新的子路径。示例: 若执行 `mvc.New(app.Party("/user")).Handle(new(user.Controller))`: - `func(*Controller) Get()` - GET:/user - `func(*Controller) Post()` - POST:/user - `func(*Controller) GetLogin()` - GET:/user/login - `func(*Controller) PostLogin()` - POST:/user/login - `func(*Controller) GetProfileFollowers()` - GET:/user/profile/followers - `func(*Controller) PostProfileFollowers()` - POST:/user/profile/followers - `func(*Controller) GetBy(id int64)` - GET:/user/{param:int64} - `func(*Controller) PostBy(id int64)` - POST:/user/{param:int64} 若执行 `mvc.New(app.Party("/profile")).Handle(new(profile.Controller))`: - `func(*Controller) GetBy(username string)` - GET:/profile/{param:string} 若执行 `mvc.New(app.Party("/assets")).Handle(new(file.Controller))`: - `func(*Controller) GetByWildcard(path string)` - GET:/assets/{param:path} 方法函数接收者支持的数据类型:int, int64, bool 和 string。 可选地,通过输出参数返回响应,如我们在依赖注入章节中所示。例如: Go ```go func(c *ExampleController) Get() string | (string, string) | (string, int) | int | (int, string) | (string, error) | error | (int, error) | (any, bool) | (customStruct, error) | customStruct | (customStruct, int) | (customStruct, string) | mvc.Result or (mvc.Result, error) ```
2
3
4
5
6
7
8
9
10
11
12
13
14
其中 mvc.Result 仅是 hero.Result 的类型别名:
type Result interface { // Dispatch 应将响应发送到上下文中的响应写入器。 Dispatch(ctx iris.Context) }
1
2
3
4
示例 这个示例相当于一个简单的 hello world 应用。看起来要额外写的代码有点不值得,但请记住:这个示例并没有用到 Iris MVC 的 Model、Persistence、View 引擎,也没用 Session,它只是为学习目的而写得非常简单,实际项目中你可能永远不会写这么简单的控制器。在我的个人笔记本上,这个示例在 "/hello" 路径上以 JSON 响应时,使用 MVC 带来的开销约为每 20MB 吞吐量多消耗 2MB,对大多数应用来说这完全可以接受。有了 Iris,你可以自行选择最适合的方式:低层级的 handler 换取性能,或高层级的 controller 换取更易维护、代码量更少的大型应用。 请仔细阅读注释。 ```go package main import ( "github.com/kataras/iris/v12" "github.com/kataras/iris/v12/mvc" "github.com/kataras/iris/v12/middleware/logger" "github.com/kataras/iris/v12/middleware/recover" ) func main() { app := iris.New() // 可选:添加两个内置的 handler, // 用于从任何 HTTP 相关的 panic 中恢复, // 并将请求日志输出到终端。 app.Use(recover.New()) app.Use(logger.New()) // 基于根 Router "/" 注册一个控制器。 mvc.New(app).Handle(new(ExampleController)) // http://localhost:8080 // http://localhost:8080/ping // http://localhost:8080/hello // http://localhost:8080/custom_path app.Listen(":8080") } // ExampleController 处理 "/"、"/ping" 和 "/hello"。 type ExampleController struct{} // Get 处理 // 方法: GET // 资源: http://localhost:8080 func (c *ExampleController) Get() mvc.Result { return mvc.Response{ ContentType: "text/html", Text: "", } } // GetPing 处理 // 方法: GET // 资源: http://localhost:8080/ping func (c *ExampleController) GetPing() string { return "pong" } // GetHello 处理 // 方法: GET // 资源: http://localhost:8080/hello func (c *ExampleController) GetHello() interface{} { return map[string]string{"message": "Hello Iris!"} } // BeforeActivation 只会被调用一次,在控制器适配到主应用之前, // 当然也是在服务器启动之前。 // 从第 9 版开始,你还可以为特定控制器的方法添加自定义路由。 // 在这里你可以注册自定义方法的 handler, // 通过 `ca.Router` 使用标准路由器完成一些不用 MVC 也能做到的事情, // 并添加会被绑定到控制器字段或方法函数入参的依赖。 func (c *ExampleController) BeforeActivation(b mvc.BeforeActivation) { anyMiddlewareHere := func(ctx iris.Context) { ctx.Application().Logger().Warnf("Inside /custom_path") ctx.Next() } b.Handle( "GET", "/custom_path", "CustomHandlerWithoutFollowingTheNamingGuide", anyMiddlewareHere, ) // 甚至可以基于该控制器的路由器添加全局中间件, // 在本例中就是根路径 "/": // b.Router().Use(myMiddleware) } // CustomHandlerWithoutFollowingTheNamingGuide 处理 // 方法: GET // 资源: http://localhost:8080/custom_path func (c *ExampleController) CustomHandlerWithoutFollowingTheNamingGuide() string { return "hello from the custom handler without following the naming guide" } // GetUserBy 处理 // 方法: GET // 资源: http://localhost:8080/user/{username:string} // By 是一个保留的"关键字",用于告诉框架你要把路径参数 // 绑定到函数的入参上,同时它也让同一个控制器里 // 可以同时存在 "Get" 和 "GetBy"。 // // func (c *ExampleController) GetUserBy(username string) mvc.Result { // return mvc.View{ // Name: "user/username.html", // Data: username, // } // } /* 可以使用不止一种,工厂会确保为该控制器的每条路由 注册正确的 HTTP 方法,需要的话取消注释即可: func (c *ExampleController) Post() {} func (c *ExampleController) Put() {} func (c *ExampleController) Delete() {} func (c *ExampleController) Connect() {} func (c *ExampleController) Head() {} func (c *ExampleController) Patch() {} func (c *ExampleController) Options() {} func (c *ExampleController) Trace() {} */ /* func (c *ExampleController) All() {} // 或者 func (c *ExampleController) Any() {} func (c *ExampleController) BeforeActivation(b mvc.BeforeActivation) { // 1 -> HTTP 方法 // 2 -> 路由路径 // 3 -> 该控制器中应作为该路由 handler 的方法名。 b.Handle("GET", "/mypath/{param}", "DoIt", optionalMiddlewareHere...) } // 激活之后,所有依赖都已设置完成——此时对它们是只读的, // 但仍然可以添加自定义控制器或普通的标准 handler。 func (c *ExampleController) AfterActivation(a mvc.AfterActivation) {} */ ```
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
控制器中所有以 HTTP 方法(Get、Post、Put、Delete 等)开头的导出函数,均可作为 HTTP 端点被调用。上述示例中,所有函数都会向响应写入字符串。注意查看每个方法前的注释。 HTTP 端点是 Web 应用中可被访问的 URL,例如 http://localhost:8080/helloworld。它由协议(HTTP)、Web 服务器的网络位置(含 TCP 端口:localhost:8080)以及目标 URI(/helloworld)共同组成。 第一条注释说明这是一个 HTTP GET 方法,通过在基础 URL 后追加 "/helloworld" 来触发。第三条注释则指明另一个 HTTP GET 方法,需追加 "/helloworld/welcome" 才能触发。 得益于 By 关键字,Controller 能够理解 GetBy 中的 "name" 参数,以及 GetWelcomeBy 中的 "name" 和 "numTimes" 参数,从而自动生成动态路由,无需编写样板代码。第三条注释所对应的动态 HTTP GET 方法,可匹配任何以 "/helloworld/welcome" 开头、后接两个路径片段的 URL:第一个片段接受任意值,第二个片段仅接受数字。例如:"http://localhost:8080/helloworld/welcome/golang/32719"。若不满足该格式,客户端将收到 404 Not Found HTTP 错误。 _examples/mvc 目录下的代码及 mvc/controller_test.go 文件,通过简洁的范例阐释了各项特性,展示了如何利用 Iris MVC Binder、Iris MVC Models 等功能。