一、mvc的根本应用
在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包
的操作实际上是基于mvc.Application
类型的实例进行的。在初始化mvc.Application实例的时候,传入参数是一个app.Party的对象,即一个路由组。因而,一个mvc.Application
实例实质上就是一个路由分组。
而后,通过mvc.Application.Handle
办法,将testController类型的对象进行注册。实际上是将controller
依赖的对象(model、service
等)注入到controller
中、将Controller
中的办法转换成路由、将controller
中执行的后果渲染到view
的过程。
2.1 从controller到ControllerActivator的转换
在第一局部的代码示例中,当初始化了mvc.Application对象后,就是通过如下这行代码对controller中的办法进行转换的:
mvcApplication.Handle(new(testController))
代码就一行,很简略。但就是这个Handle函数将testController中的办法转换成了路由,使用户能够通过对应的门路拜访到controller中的办法。接下来咱们看看该Handle办法中都做了哪些事件。
点击进入源代码,直到iris/mvc/mvc.go
文件中的handle
办法,如下图示所示。
该办法显示,首先将controller包装成了一个ControllerActivator对象c。其构造体如下:
各字段含意如下:
- injector:该controller中依赖的字段类型。即该controller构造体中有哪些字段。这个咱们前面具体解释。
- Value:该字段即在一开始new进去的controller的对象值。比方new(testController)的值。
- Typ:该字段是controller的具体类型。比方下面示例中的testController类型。
- routes:controller中每个函数名对应的具体的路由。
而后该对象c做了3件事件:执行对象c的BeforeActivation办法(如果controller中定义了该办法)、activate办法和AfterActivation办法(如果定义了该办法)。咱们先跳过BeforeActivation和AfterActivation办法,重点看下activate办法。
2.2 ControllerActivator的activate办法
c.activate
办法的源码如下图所示,次要是parseMethods办法,依据名字也能猜到是要解析办法了。
咱们再进入c.parseMethods办法的源代码,如下:
这里的逻辑也很简略,先是获取该类型(即controller
的构造体类型,例如示例中的testController
类型)的办法个数,而后顺次遍历所有的办法,并对每个办法进行解析,也就是代码中的c.parseMethod(m)
办法。
接下来进入到c.parseMethod(m)
的代码逻辑中,看看该办法做了些什么。
这里看到,通过parseMethod
解析进去了申请的办法名称httpMethod(例如GET、POST等)、申请的门路httpPath。而后再通过c.Handle函数将对应的申请办法和申请门路以及办法名注册成iris的惯例路由。
到这里咱们先总结一下controller转换的过程:
- 首先,先将controller对象封装成ControllerActivator对象。该对象蕴含了controller类型的值以及具体的controller数据类型。
- 其次,通过reflect包获取该controller类型的有哪些办法,并顺次遍历解析这些办法。
- 而后,依据办法名称解析出规范的HTTP申请的办法以及申请门路,即代码中的parseMethord函数。
- 最初,将该申请办法以及申请门路转换成iris惯例的路由,即代码中的c.Handle函数。
因为申请办法和申请门路是依据controller中的办法名称解析进去的,所以开发人员在给controller的办法的命名时,须要遵循以下规定:
- controller中的办法必须采纳驼峰式命名,且首字母必须大小。
- parseMethod会大写字母为宰割符,将办法名宰割成多个单词。例如。GetHome会宰割成Get、Home两个单词。办法名HOME,则会被宰割成H、O、M、E四个单词。具体实现算法可查看源代码methodLexer.reset函数。
- 办法名的第一个单词必须是http申请的办法名。无效的申请办法为GET、HEAD、POST、PUT、PATCH、DELETE、CONNECT、OPTIONS、TRACE。具体的实现可查看源码申请办法校验逻辑。
- 从办法名的第二个单词开始,应用
"/"
符号将各个单词连接成对应的申请门路。所以办法名实际上是由申请办法+申请门路模式组成的。
例如在testController中有如下GetMyHome办法,那么,对应的申请门路是/my/home
。申请http://localhost:8080/my/home
就会执行testController的GetMyHome办法的逻辑。
- 在办法名中能够通过
By
能够在路由中减少动静参数。
例如在testController中有如下办法GetByHome(username string),则对应的申请门路会被转换成 /{param1:string}/home。申请http://localhost:8080/yufuzi/... 就能拜访到testController中的GetByHome函数的逻辑,并且在该办法中username
的变量值就是门路中的yufizi
。如下:
- 若By在办法名的两头地位,一个By只对应一个动静参数;若By在办法名的最初,则办法中的所有输出参数都被解析成门路中的动静参数。
示例一: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的办法转换成具体的路由申请处理器。
2.3 ControllerActivator的Handle办法 -- 将controller中的办法转换成申请处理器
咱们晓得iris的规范路由处理器是type Handler func(*Context)类型
的这一个函数。那在mvc中,是如何将controller中的办法转换成这样规范的申请处理器类型的呢?
这个转换次要在ControllerActivator.parseMethod办法的第二局部的性能:ControllerActivator.Handle。进入该函数的源代码,直到ControllerActivator.handleMany函数,如下:
在handleMany函数中次要做了两件事:
- 将函数转换成申请处理器。即c.handlerOf函数做的事件
- 将申请解决注册成规范的路由。即c.app.Router.HandleMany函数做的事件。
这里咱们次要看c.handlerOf
是如何将函数转换申请处理器的。至于注册成规范路由,大家能够参考这篇iris路由相干的文章:深刻了解iris路由的底层实现原理。
2.4 将controller的函数转换成规范的申请处理器类型
解决该性能的是逻辑在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的高级应用,敬请期待。
特地举荐:一个专一go我的项目实战、我的项目中踩坑教训及避坑指南、各种好玩的go工具的公众号,「Go学堂」,专一实用性,十分值得大家关注。点击下方公众号卡片,间接关注。关注送《100个go常见的谬误》pdf文档。