Posted at

SkinnyFrameworkでControllerの共通処理はtraitにしてmixinする構成がよさそう

More than 3 years have passed since last update.

SkinnyではControllerを作るにあたって基底として利用できるクラスが多数あります。作成したいControllerの機能に応じて使い分けることになります。


  • ScalatraFilter系


    • SkinnyController (基本これを使う)

    • SkinnyApiController (画面を持たないWebAPIを作成する場合に使う)

    • SkinnyResource (モデルに対する定型のCRUDを作る場合に使う)

    • SkinnyApiResource (モデルに対するREST APIを作る場合に使う)



  • ScalatraServlet系


    • SkinnyServlet (SkinnyControllerのServlet版)

    • SkinnyApiServlet (SkinnyApiControllerのServlet版)

    • SkinnyResourceServlet (SkinnyResourceのServlet版)

    • SkinnyApiResourceServlet (SkinnyApiResourceのServlet版)



大きくは、ScalatraFilter系とScalatraServlet系があります。

これらは以下のような違いがあります。


  • FormからのファイルアップロードはScalatraServlet系のみが受け取れる

  • マウント方法が異なる

object Controllers {

def mount(ctx: ServletContext): Unit = {
filter.mount(ctx)

org.scalatra.servlet.RichServletContext(ctx).mount(servlet, "/servlet")
}

// ScalatraFileter系コントローラ
object filter extends SkinnyFilter with Routes {
// URLはアプリルートからの絶対パスを指定する
val hogeUrl = get("/filter/hoge")(hoge).as('hoge)
}

// ScalatraServlet系コントローラ
object servlet extends SkinnyServlet with Routes {
// mount時のパスからの相対パスを指定する
val fugaUrl = get("/fuga")(fuga).as('fuga) // 実URLは、"/servlet/hoge"
}

}

Skinnyのブランクアプリではアプリ用のコントローラの基底クラスとしてSkinnyControllerを継承したApplicationControllerと、SkinnyApiControllerを継承したApiControllerが用意されています。

アプリのコントローラで共通の機能が必要なときは、これらのクラスに実装することになります。

しかし、前述のとおり他のコントローラ基底クラスを使いたい場合に、ここで実装した共通機能を使えなくなってしまします。

そこで、Controllerの共通処理はtraitとして実装しておき、必要に応じてそのtraitと上記コントローラ基底クラスとを組み合わせられるようにしておくとよいと思います。

例えば、以下のようにコントローラの種類ごとのFeatureを用意しておくと後々で拡張しやすくなります。

/** アプリケーションの画面ありControllerの基底クラス */

trait ApplicationControllerFeature extends ApplicationApiControllerFeature
with ErrorPageFilter {

}

/** アプリ全体のAPI系コントローラ共通機能 */
trait ApplicationApiControllerFeature
// 基本となるtraitをmixin
extends SkinnySessionFilter
with TxPerRequestFilter {

// すべてのコントローラに対するbefore処理
before() {
logger.info("ACCESS_LOG params=" + JSONStringOps.toJSONStringAsIs(params))
}
}

// ユーザエリア用

trait UserControllerFeature extends ApplicationControllerFeature with UserControllerBaseFeature
trait UserApiControllerFeature extends ApplicationApiControllerFeature with UserControllerBaseFeature

/** ユーザエリア向けControllerの基盤機能 */
sealed trait UserApiControllerBaseFeature
// ユーザページ固有のフィルタ
extends UserLoginFilter { self: ApplicationApiControllerFeature =>
}

// 管理画面用

trait AdminControllerFeature extends ApplicationControllerFeature with AdminApiControllerBaseFeature
trait AdminApiControllerFeature extends ApplicationApiControllerFeature with AdminControllerBaseFeature

/** 管理画面向けControllerの基盤機能 */
sealed trait AdminApiControllerBaseFeature
// 管理ページ固有のフィルタ
extends AdminLoginFilter { self: ApplicationApiControllerFeature =>
}