beego的控制器Controller篇 — 运行机制

发布时间:2024年01月16日

1?Controller运行机制

源码分析入手:

type MainController struct {
	beego.Controller
}

func (c *MainController) Get() {
	c.Data["Website"] = "beego.vip"
	c.Data["Email"] = "astaxie@gmail.com"
	c.TplName = "index.tpl"
}

声明了一个控制器 MainController,这个控制器里面内嵌了 beego.Controller,这就是 Go 的嵌入方式,也就是 MainController 自动拥有了所有 beego.Controller 的方法。

而 beego.Controller 拥有很多方法,其中包括 Init、Prepare、Post、Get、Delete、Head 等方法。可以通过重写的方式来实现这些方法,而我们上面的代码就是重写了 Get 方法,当get请求时,就会执行MainController的Get方法。当然要满足执行beego.Controller的重写方法,则注册路由方式可以这样写:

beego.Router("/", &controllers.MainController{})

用户也可以改变这个行为,通过注册自定义的函数名里面的代码是需要执行的逻辑,通过各种方式获取数据,然后赋值到 c.Data 中,这是一个用来存储输出数据的 map,可以赋值任意类型的值,这里我们只是简单举例输出两个字符串。

注册自定义的函数名:(更多路由配置请参考文章《路由设置篇》)

beego.Router("/api/list",&RestController{},"Get:ListFood")

c.TplName 就是需要渲染的模板,这里指定了 index.tpl,如果用户不设置该参数,那么默认会去到模板目录的 Controller/<方法名>.tpl 查找,例如上面的方法会去 maincontroller/get.tpl (文件、文件夹必须小写)。

用户设置了模板之后系统会自动的调用 Render 函数(这个函数是在 beego.Controller 中实现的),所以无需用户自己来调用渲染。

也可以不使用模版,直接用 this.Ctx.WriteString 输出字符串,如:

func (this *MainController) Get() {
        this.Ctx.WriteString("hello")
}

2?控制器函数

2.1 基本Controller函数

beego.Controller?实现了接口?beego.ControllerInterfacebeego.ControllerInterface?定义了如下函数:

  • Init(ct *context.Context, childName string, app interface{})

    这个函数主要初始化了 Context、相应的 Controller 名称,模板名,初始化模板参数的容器 Data,app 即为当前执行的 Controller 的 reflecttype,这个 app 可以用来执行子类的方法。

  • Prepare()

    这个函数主要是为了用户扩展用的,这个函数会在下面定义的这些 Method 方法之前执行,用户可以重写这个函数实现类似用户验证之类。

  • Get()

    如果用户请求的 HTTP Method 是 GET,那么就执行该函数,默认是 405,用户继承的子 struct 中可以实现了该方法以处理 Get 请求。

  • Post()

    如果用户请求的 HTTP Method 是 POST,那么就执行该函数,默认是 405,用户继承的子 struct 中可以实现了该方法以处理 Post 请求。

  • Delete()

    如果用户请求的 HTTP Method 是 DELETE,那么就执行该函数,默认是 405,用户继承的子 struct 中可以实现了该方法以处理 Delete 请求。

  • Put()

    如果用户请求的 HTTP Method 是 PUT,那么就执行该函数,默认是 405,用户继承的子 struct 中可以实现了该方法以处理 Put 请求.

  • Head()

    如果用户请求的 HTTP Method 是 HEAD,那么就执行该函数,默认是 405,用户继承的子 struct 中可以实现了该方法以处理 Head 请求。

  • Patch()

    如果用户请求的 HTTP Method 是 PATCH,那么就执行该函数,默认是 405,用户继承的子 struct 中可以实现了该方法以处理 Patch 请求.

  • Options()

    如果用户请求的HTTP Method是OPTIONS,那么就执行该函数,默认是 405,用户继承的子 struct 中可以实现了该方法以处理 Options 请求。

  • Finish()

    这个函数是在执行完相应的 HTTP Method 方法之后执行的,默认是空,用户可以在子 struct 中重写这个函数,执行例如数据库关闭,清理数据之类的工作。

  • Render() error

    这个函数主要用来实现渲染模板,如果 beego.AutoRender 为 true 的情况下才会执行。

所以通过子 struct 的方法重写,用户就可以实现自己的逻辑,接下来我们看一个实际的例子:

type AddController struct {
    beego.Controller
}

func (this *AddController) Prepare() {
}

func (this *AddController) Get() {
    this.Data["content"] = "value"
    this.Layout = "admin/layout.html"
    this.TplName = "admin/add.tpl"
}

func (this *AddController) Post() {
    pkgname := this.GetString("pkgname")
    content := this.GetString("content")
    .....
    this.Ctx.Redirect(302, "/admin/index")
}

构建自己的基类 baseController:

type NestPreparer interface {
        NestPrepare()
}

// baseRouter implemented global settings for all other routers.
type baseController struct {
        beego.Controller
        i18n.Locale
        user    models.User
        isLogin bool
}
// Prepare implemented Prepare method for baseRouter.
func (this *baseController) Prepare() {
        // page start time
        this.Data["PageStartTime"] = time.Now()

        // Setting properties.
        this.Data["AppDescription"] = utils.AppDescription
        this.Data["AppKeywords"] = utils.AppKeywords
        this.Data["AppName"] = utils.AppName
        this.Data["AppVer"] = utils.AppVer
        this.Data["AppUrl"] = utils.AppUrl
        this.Data["AppLogo"] = utils.AppLogo
        this.Data["AvatarURL"] = utils.AvatarURL
        this.Data["IsProMode"] = utils.IsProMode

        if app, ok := this.AppController.(NestPreparer); ok {
                app.NestPrepare()
        }
}

判断当前运行的 Controller 是否是 NestPreparer 实现,如果是的话调用子类的方法

type BaseAdminRouter struct {
    baseController
}

func (this *BaseAdminRouter) NestPrepare() {
    if this.CheckActiveRedirect() {
            return
    }

    // if user isn't admin, then logout user
    if !this.user.IsAdmin {
            models.LogoutUser(&this.Controller)

            // write flash message
            this.FlashWrite("NotPermit", "true")

            this.Redirect("/login", 302)
            return
    }

    // current in admin page
    this.Data["IsAdmin"] = true

    if app, ok := this.AppController.(ModelPreparer); ok {
            app.ModelPrepare()
            return
    }
}

func (this *BaseAdminRouter) Get(){
    this.TplName = "Get.tpl"
}

func (this *BaseAdminRouter) Post(){
    this.TplName = "Post.tpl"
}

基于上面的设计,首先执行 Prepare,这个就是 Go 语言中 struct 中寻找方法的顺序,依次往父类寻找。执行 BaseAdminRouter 时,查找他是否有 Prepare 方法,没有就寻找 baseController,找到了,那么就执行逻辑,然后在 baseController 里面的 this.AppController 即为当前执行的控制器 BaseAdminRouter,因为会执行 BaseAdminRouter.NestPrepare 方法。然后开始执行相应的 Get 方法或者 Post 方法。

2.2 控制函数

在应用中经常会遇到这样的情况,在 Prepare 阶段进行判断,如果用户认证不通过,就输出一段信息,然后直接中止进程,之后的 Post、Get 之类的不再执行,那么如何终止呢?可以使用 StopRun 来终止执行逻辑,可以在任意的地方执行。

type RController struct {
    beego.Controller
}

func (this *RController) Prepare() {
    this.Data["json"] = map[string]interface{}{"name": "astaxie"}
    this.ServeJSON()
    this.StopRun()
}

调用 StopRun 之后,如果你还定义了 Finish 函数就不会再执行,如果需要释放资源,那么请自己在调用 StopRun 之前手工调用 Finish 函数。

2.3 在表单中使用 PUT 方法

在表单提交设置如下参数

<form method="post" ...>
  <input type="hidden" name="_method" value="put" />

接着在 Beego 中添加一个过滤器来判断是否将请求当做 PUT 来解析:

var FilterMethod = func(ctx *context.Context) {
    if ctx.BeegoInput.Query("_method")!="" && ctx.BeegoInput.IsPost(){
          ctx.Request.Method = ctx.BeegoInput.Query("_method")
    }
}

beego.InsertFilter("*", beego.BeforeRouter, FilterMethod)

当然参数不一定非得_method,不过通过最新前后端分离的请求方式,前端已经支持PUT请求,这里知识介绍一下拦截器作用

参考文章:

https://www.fansimao.com/800468.html

https://www.xichangyou.com/800512.html

文章来源:https://blog.csdn.net/liu289747235/article/details/135636567
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。