一、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 就能拜访到testControllerGetHome办法。

咱们晓得,在iris框架中,是须要注册路由的,行将门路对应到特定的context.Handler类型(函数类型)的处理器,当用户拜访对应的门路时,能力执行对应处理器上的函数体的逻辑的。那么,mvc包是如何做到门路到controller中函数的映射的呢?

二、mvc的实现原理

通过下面应用基于mvc包的示例,咱们能够理解到,mvc包的操作实际上是基于mvc.Application类型的实例进行的。在初始化mvc.Application实例的时候,传入参数是一个app.Party的对象,即一个路由组。因而,一个mvc.Application实例实质上就是一个路由分组。

而后,通过mvc.Application.Handle办法,将testController类型的对象进行注册。实际上是controller依赖的对象(model、service等)注入到controllerController中的办法转换成路由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文档。