在iris中,还封装了mvc包,该包可以让开发者快速的搭建出基于mvc(model-view-controller)分层的业务系统。其基本使用如下:
package mainimport (
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/context"
"github.com/kataras/iris/v12/mvc"
)
func main() {
app := iris.New()
// 基于app.Party("/")分组 初始化mvcApplication
mvcApplication := mvc.New(app.Party("/"))
// 注册controller
mvcApplication.Handle(new(testController))
app.Listen(":8080")
}
// 定义controller处理器
type testController struct {
Ctx *context.Context
}
func (c *testController) GetHome() {
c.Ctx.Writef(ctx.Method())
}
func (c *testController) Post() {
c.Ctx.Writef(ctx.Method())
}
这样,就能搭建一个最简单的controller处理器了。然后运行该服务,在浏览器中输入 http://localhost:8080/home
就能访问到testController
的GetHome
方法。
我们知道,在iris框架中,是需要注册路由的,即将路径对应到特定的context.Handler类型
(函数类型)的处理器,当用户访问对应的路径时,才能执行对应处理器上的函数体的逻辑的。那么,mvc包
是如何做到路径到controller
中函数的映射的呢?
通过上面使用基于mvc包
的示例,我们可以了解到,mvc包
的操作实际上是基于mvc.Application
类型的实例进行的。在初始化mvc.Application实例的时候,传入参数是一个app.Party的对象,即一个路由组。因此,一个mvc.Application
实例本质上就是一个路由分组。
然后,通过mvc.Application.Handle
方法,将testController类型的对象进行注册。实际上是将controller
依赖的对象(model、service
等)注入到controller
中、将Controller
中的方法转换成路由、将controller
中执行的结果渲染到view
的过程。
在第一部分的代码示例中,当初始化了mvc.Application对象后,就是通过如下这行代码对controller中的方法进行转换的:
mvcApplication.Handle(new(testController))
代码就一行,很简单。但就是这个Handle函数将testController中的方法转换成了路由,使用户可以通过对应的路径访问到controller中的方法。接下来我们看看该Handle方法中都做了哪些事情。
点击进入源代码,直到iris/mvc/mvc.go
文件中的handle
方法,如下图示所示:该方法显示,首先将controller包装成了一个ControllerActivator对象c。其结构体如下:
各字段含义如下:
然后该对象c做了3件事情:
执行对象c的BeforeActivation方法(如果controller中定义了该方法)
activate方法
和AfterActivation方法(如果定义了该方法)。
我们先跳过BeforeActivation和AfterActivation方法,重点看下activate方法。
c.activate
方法的源码如下图所示,主要是parseMethods方法,根据名字也能猜到是要解析方法了。
我们再进入c.parseMethods方法的源代码,如下:
这里的逻辑也很简单,先是获取该类型(即controller
的结构体类型,例如示例中的testController
类型)的方法个数,然后依次遍历所有的方法,并对每个方法进行解析,也就是代码中的c.parseMethod(m)
方法。
接下来进入到c.parseMethod(m)
的代码逻辑中,看看该方法做了些什么。
这里看到,通过parseMethod
解析出来了请求的方法名称httpMethod(例如GET、POST等)、请求的路径httpPath。然后再通过c.Handle函数将对应的请求方法和请求路径以及方法名注册成iris的常规路由。
到这里我们先总结一下controller转换的过程:
因为请求方法和请求路径是根据controller中的方法名称解析出来的,所以开发人员在给controller的方法的命名时,需要遵循以下规则:
例如在testController中有如下GetMyHome方法,那么,对应的请求路径是/my/home
。请求http://localhost:8080/my/home
就会执行testController的GetMyHome方法的逻辑。
例如在testController中有如下方法GetByHome(username string),则对应的请求路径会被转换成 /{param1:string}/home。请求http://localhost:8080/yufuzi/home 就能访问到testController中的GetByHome函数的逻辑,并且在该方法中username
的变量值就是路径中的yufizi
。如下:
示例一:By在方法名中间位置
GetByHome方法名中有两个参数,那么,访问该路径http://localhost:8080/yufuzi/home
时就会报panic。如下:
func (c *testController) GetByHome(username string, param2 int) {
c.Ctx.Writef(c.Ctx.Method() + " " + username + " Home")
c.Ctx.Writef("param2:" + param2) //这里param2是对应类型的默认值:0
}
示例二:By在方法名最后位置
GetHomeBy方法名中有两个参数,则会转换成对应的路径 /home/{param1:string}/{param2:int}
。如下:
func (c *testController) GetHomeBy(username string, param2 int) {
c.Ctx.Writef(c.Ctx.Method() + " " + username + " Home")
c.Ctx.Writef("param2:" + param2)
}
以上是对controller中方法的解析以及命名时需要遵守的规则。接下来,我们继续看如何将controller的方法转换成具体的路由请求处理器。
我们知道iris的标准路由处理器是type Handler func(*Context)�类型
的这一个函数。那在mvc中,是如何将controller中的方法转换成这样标准的请求处理器类型的呢?
这个转换主要在ControllerActivator.parseMethod方法的第二部分的功能:ControllerActivator.Handle。进入该函数的源代码,直到ControllerActivator.handleMany函数,如下:
在handleMany函数中主要做了两件事:
这里我们主要看c.handlerOf
是如何将函数转换请求处理器的。至于注册成标准路由,大家可以参考这篇iris路由相关的文章:深入理解iris路由的底层实现原理。
处理该功能的是逻辑在handlerOf
函数中。下面是handlerOf
的源代码,我们看到主要做了两件事:一是attachInjector函数;一个是injector中的MethodHandler函数。
实际将controller的函数转换成请求处理器的是在injector的MethodHandler中进行的,即返回的handler对象。而injector就是在第一步的c.attachInjector函数中构造出来的。
那么,c.injector是什么呢?下面我们看下其对应的结构体,如下图:
可以看出来,injector实际上是对controller具体类型及其对象的描述。还是以testController为例,在Struct.ptrType就是代表testController这种数据类型;Struct.ptrValue代表的是testController对象值;bindings代表的是在testController中有哪些字段值。比如testController中要是定义如下:
type testController struct {
model *userModel
}
那么,testController
对象就需要绑定userModel类型的值。在实例化testController
时,需要将一个具体的userModel
类型的值赋值给model
变量,这样在testController
的方法中就能引用该变量从数据源中读取数据。而这种依赖就是保存在bingdings
中的。
我们主要看第二部分,c.injector.MethodHandler
将函数转换成路由处理器类型。点击进入源码,直到/iris/hero/handler.go
文件的makeHandler
函数,如下所示(注:这里为了突出最终的返回值,省略了一些代码)。
首先,makeHandler
返回值就是一个context.Handler
类型的函数。在函数体内有3部分:获取controller
函数的输入参数值、通过v.Call
函数调用controller
实际的函数体、通过dispatchFuncResult
分发函数的返回值。
到此,就把controller
中的函数转换成了标准的路由处理器类型context.Handler
。并且,只有controller中的方法名的第一个单词是HTTP标准的请求方法(GET、HEAD、POST、PUT、PATCH、DELETE、CONNECT、OPTIONS、TRACE)时,才会将该方法自动注册成对应的路由。
在了解了mvc的实现原理后,下一篇我们讲解mvc的高级使用,敬请期待。
推荐阅读