Help us understand the problem. What is going on with this article?

RevelでInterceptorsを使って共通処理を書く

More than 3 years have passed since last update.

まえがき

私は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テンプレートに渡していますが、これも共通化してみましょう。
まずは親コントローラに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}}

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした