1. kamykn

    No comment

    kamykn
Changes in body
Source | HTML | Preview

まえがき

私はGolangで開発しているときにはRevelを利用しているのですが、サイト全体でわざわざ記述を追加する事なくやっておいて欲しい共通処理をどこに書くのか迷ったので、個人的に至った結論をまとめていきたいと思います。

Overview

RevelではInterceptorsを使ってコントローラの前処理、後処理を記述することが出来ます。
Golangはconstructが無いため、今回のようにコントローラのアクションの前処理を実行するにはこのような仕組みを利用します。

今回はコントローラ拡張によって親相当のコントローラを作成し、そこにInterceptorsをセットしていきます。

前知識

Revelのコントローラ拡張

コントローラの拡張で親相当のコントローラが作成できます。
https://revel.github.io/quickstart/controllers.html#extending-the-controller

※ 子コントローラでは、親コントローラの参照はポインタではないことに注意

type (
    BaseController struct {
        *revel.Controller
    }
)
type (
    MyController struct {
        BaseController
    }
)

RevelのInterceptors

今回はMethod Interceptorを使います。
レシーバーに今回は親コントローラを設定するつもりです。
下記はドキュメントサイトにあるサンプル( https://revel.github.io/manual/interceptors.html#method-interceptor )。

func (c Hotels) checkUser() revel.Result {
    if user := connected(c); user == nil {
        c.Flash.Error("Please log in first")
        return c.Redirect(App.Index)
    }
    return nil
}

func init() {
    revel.InterceptMethod(Hotels.checkUser, revel.BEFORE)
}

本題:共通処理を書く

今回はMyappというアプリケーションを作成し、ログインしているアカウントデータをすべてのコントローラで扱えるようなものを作ってみましょう。

親コントローラ作成

myapp/app/controllers/core/baseController.go
package core

import (
    "github.com/revel/revel"
    "myapp/app/models"
)

type BaseController struct {
    *revel.Controller
    Account struct {
        models.Account // モデルに定義してあるAccountのデータの型
        IsLogin bool
    }
}

func (b *BaseController) setAccountData() revel.Result {
    // gormのモデルを利用している
    accountModel := models.NewAccountModel()

    // アカウント取得
    // Revelのセッションを使ってAccountIDを取得。説明は割愛
    accountID :=  b.GetAccountID()
    b.Account.Account = accountModel.GetByID(accountID) 

    // ログインフラグを追加
    b.Account.IsLogin = true

    // Account型にモデルで定義しているメソッド
    if b.Account.IsEmpty() {
        b.Account.IsLogin = false
    }

    return nil
}

子となるコントローラで親となるコントローラを継承します。

myapp/app/controllers/app.go
package controllers

import (
    "github.com/revel/revel"
    "myapp/app/controllers/core"
)

type App struct {
    core.BaseController
}

func (a App) Index() revel.Result {
    account := a.Account

    // Viewでアカウント情報を使いたい
    return a.Render(account)
}

Interceptorsを設定

下記を親コントローラに追記します。

myapp/app/controllers/core/baseController.go
func init () {
    revel.InterceptMethod((*BaseController).setAccountData, revel.BEFORE)
}

あとはコントローラを増やすたびに親コントローラーを継承するだけ

これで、共通処理化の準備ができました。後はログインチェックが必要なコントローラでベースコントローラを継承していくだけで共通処理として利用できるようになっていきます。

おまけ:共通でViewテンプレートをに渡す引数を設定する

上記ではわざわざRenderからaccountからVewテンプレートに渡していますが、これも共通化してみましょう。上記ではわざわざRenderからaccount変数をVewテンプレートに渡していますが、これも共通化してみましょう。
まずは親コントローラにinterceptor用のメソッドを追加します。

myapp/app/controllers/app.go
func (b *BaseController) addAccountToRender() revel.Result {
    // RenderArgsにはViewに渡される変数がセットされている
    b.RenderArgs["account"] = b.Account

    return nil
}

そしてinterceptorをセットします。

myapp/app/controllers/core/baseController.go
func init () {
    revel.InterceptMethod((*AuthBaseController).setAccountData, revel.BEFORE)
    revel.InterceptMethod((*AuthBaseController).addAccountToRender, revel.AFTER)
}

今度はAFTERに設定してみました。
子コントローラの方ではaccountに関する記述は削除します。

myapp/app/controllers/app.go
func (c App) Index() revel.Result {
    return c.Render()
}

あとはViewテンプレートで

myapp/app/app/index.html
{{.account}}

などとしてみると中身が渡っているのが確認できるかと思います。