name: 控制器函数

sort: 3

控制器介绍

提示:在 v1.6 中,此文档所涉及的 API 有重大变更,this.ServeJson() 更改为 this.ServeJSON()this.TplNames 更改为 this.TplName

基于 beego 的 Controller 设计,只需要匿名组合 beego.Controller 就可以了,如下所示:

  1. type xxxController struct {
  2. beego.Controller
  3. }

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 的方法重写,用户就可以实现自己的逻辑,接下来我们看一个实际的例子:

  1. type AddController struct {
  2. beego.Controller
  3. }
  4. func (this *AddController) Prepare() {
  5. }
  6. func (this *AddController) Get() {
  7. this.Data["content"] = "value"
  8. this.Layout = "admin/layout.html"
  9. this.TplName = "admin/add.tpl"
  10. }
  11. func (this *AddController) Post() {
  12. pkgname := this.GetString("pkgname")
  13. content := this.GetString("content")
  14. pk := models.GetCruPkg(pkgname)
  15. if pk.Id == 0 {
  16. var pp models.PkgEntity
  17. pp.Pid = 0
  18. pp.Pathname = pkgname
  19. pp.Intro = pkgname
  20. models.InsertPkg(pp)
  21. pk = models.GetCruPkg(pkgname)
  22. }
  23. var at models.Article
  24. at.Pkgid = pk.Id
  25. at.Content = content
  26. models.InsertArticle(at)
  27. this.Ctx.Redirect(302, "/admin/index")
  28. }

从上面的例子可以看出来,通过重写方法可以实现对应 method 的逻辑,实现 RESTful 结构的逻辑处理。

下面我们再来看一种比较流行的架构,首先实现一个自己的基类 baseController,实现一些初始化的方法,然后其他所有的逻辑继承自该基类:

  1. type NestPreparer interface {
  2. NestPrepare()
  3. }
  4. // baseRouter implemented global settings for all other routers.
  5. type baseController struct {
  6. beego.Controller
  7. i18n.Locale
  8. user models.User
  9. isLogin bool
  10. }
  11. // Prepare implemented Prepare method for baseRouter.
  12. func (this *baseController) Prepare() {
  13. // page start time
  14. this.Data["PageStartTime"] = time.Now()
  15. // Setting properties.
  16. this.Data["AppDescription"] = utils.AppDescription
  17. this.Data["AppKeywords"] = utils.AppKeywords
  18. this.Data["AppName"] = utils.AppName
  19. this.Data["AppVer"] = utils.AppVer
  20. this.Data["AppUrl"] = utils.AppUrl
  21. this.Data["AppLogo"] = utils.AppLogo
  22. this.Data["AvatarURL"] = utils.AvatarURL
  23. this.Data["IsProMode"] = utils.IsProMode
  24. if app, ok := this.AppController.(NestPreparer); ok {
  25. app.NestPrepare()
  26. }
  27. }

上面定义了基类,大概是初始化了一些变量,最后有一个 Init 函数中那个 app 的应用,判断当前运行的 Controller 是否是 NestPreparer 实现,如果是的话调用子类的方法,下面我们来看一下 NestPreparer 的实现:

  1. type BaseAdminRouter struct {
  2. baseController
  3. }
  4. func (this *BaseAdminRouter) NestPrepare() {
  5. if this.CheckActiveRedirect() {
  6. return
  7. }
  8. // if user isn't admin, then logout user
  9. if !this.user.IsAdmin {
  10. models.LogoutUser(&this.Controller)
  11. // write flash message
  12. this.FlashWrite("NotPermit", "true")
  13. this.Redirect("/login", 302)
  14. return
  15. }
  16. // current in admin page
  17. this.Data["IsAdmin"] = true
  18. if app, ok := this.AppController.(ModelPreparer); ok {
  19. app.ModelPrepare()
  20. return
  21. }
  22. }
  23. func (this *BaseAdminRouter) Get(){
  24. this.TplName = "Get.tpl"
  25. }
  26. func (this *BaseAdminRouter) Post(){
  27. this.TplName = "Post.tpl"
  28. }

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

提前终止运行

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

  1. type RController struct {
  2. beego.Controller
  3. }
  4. func (this *RController) Prepare() {
  5. this.Data["json"] = map[string]interface{}{"name": "astaxie"}
  6. this.ServeJSON()
  7. this.StopRun()
  8. }

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

在表单中使用 PUT 方法

首先要说明, 在 XHTML 1.x 标准中, 表单只支持 GET 或者 POST 方法. 虽然说根据标准, 你不应该将表单提交到 PUT 方法, 但是如果你真想的话, 也很容易, 通常可以这么做:

首先表单本身还是使用 POST 方法提交, 但是可以在表单中添加一个隐藏字段:

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

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

  1. var FilterMethod = func(ctx *context.Context) {
  2. if ctx.BeegoInput.Query("_method")!="" && ctx.BeegoInput.IsPost(){
  3. ctx.Request.Method = ctx.BeegoInput.Query("_method")
  4. }
  5. }
  6. beego.InsertFilter("*", beego.BeforeRouter, FilterMethod)