Go
Revel

Revelの主な処理の流れ

More than 3 years have passed since last update.

Go製フレームワークRevelの処理の流れです。
ざっくりとした処理の流れの話です。

以下、社内勉強会用資料。

revelの主な処理の流れ

RevelDesign.png
http://revel.github.io/manual/concepts.html

Play Frameworkを参考に作っている。
基本的にはMVC。

revel アプリの作成方法

revel アプリケーションの作成

$ revel new sample
~
~ revel! http://revel.github.io
~
Your application is ready:
   /Users/username/.go/src/sample

You can run it with:
   revel run sample

$GOPATH/src/sample のファイル構成

.
├── app
│   ├── controllers
│   │   └── app.go
│   ├── init.go
│   └── views
│       ├── App
│       │   └── Index.html
│       ├── debug.html
│       ├── errors
│       │   ├── 404.html
│       │   └── 500.html
│       ├── flash.html
│       ├── footer.html
│       └── header.html
├── conf
│   ├── app.conf
│   └── routes
├── messages
│   └── sample.en
├── public
│   ├── css
│   │   └── bootstrap.css
│   ├── img
│   │   ├── favicon.png
│   │   ├── glyphicons-halflings-white.png
│   │   └── glyphicons-halflings.png
│   └── js
│       └── jquery-1.9.1.min.js
└── tests
    └── apptest.go

revel の実行

$ revel run sample

起動してアクセスをすると、app/routes/routes.goapp/tmp/main.go にファイルが作られている。

.
├── app
│   ├── controllers
│   │   └── app.go
│   ├── init.go
│   ├── routes # ここと
│   │   └── routes.go
│   ├── tmp    # ここが新しい
│   │   └── main.go
│   └── views
# 省略

revel の内部構造

revel.png

https://github.com/hirokidaichi/goviz で出力

各構成

  • main: コマンドツール
  • harness: アプリケーションのビルドやwatchなどを行う
  • revel: コア
  • config: コンフィグ設定
  • websocket: websocket
  • pathtree: routerのマッチング /:userId のマッチング
  • simpleuuid: uuid生成
  • fsnotify: fileのwatchで利用
  • gocolorize: コンソールに色つけ

Harness

  • ユーザープログラムをパースして、起動コントローラとなるmain.goを生成して、ユーザーアプリケーションサーバーを起動する
  • ユーザープログラムのbuildとrun。コンパイルエラーの表示。
  • rebuildやモニタリングもここ経由で表示

watch

  • app側の config/app.conf のwatchの状態によって、harness側の挙動が変わる
    • harness用のport開いたり

build

github.com/revel/revel/harness/build.go 内でビルドしている.

// Generate two source files.
templateArgs := map[string]interface{}{
     "Controllers":    sourceInfo.ControllerSpecs(),
     "ValidationKeys": sourceInfo.ValidationKeys,
     "ImportPaths":    calcImportAliases(sourceInfo),
     "TestSuites":     sourceInfo.TestSuites(),
}

genSource("tmp", "main.go", MAIN, templateArgs)
genSource("routes", "routes.go", ROUTES, templateArgs)

https://github.com/revel/revel/blob/master/harness/build.go#L288 にmain.go。
https://github.com/revel/revel/blob/master/harness/build.go#L341 にroutes.goのテンプレートがある

main.go作成後

github.com/revel/revel/harness/app.go 側からオプションを渡してmain.goを実行

github.com/revel/revel/harness/app.go
// AppCmd manages the running of a Revel app server.
// It requires revel.Init to have been called previously.
type AppCmd struct {
  *exec.Cmd
}

//
// (省略)
//

// Run the app server inline.  Never returns.
func (cmd AppCmd) Run() {
  revel.TRACE.Println("Exec app:", cmd.Path, cmd.Args)
  if err := cmd.Cmd.Run(); err != nil {
    revel.ERROR.Fatalln("Error running:", err)
  }
}

main.go側の処理

github.com/revel/revel/server.gofunc Run() があり、なんだかんだでそれが走る。

リクエストを受け付けると handlerが実行され
revel.Controllerとrevel.Request, revel.Responseが作成されて
各Filtersを実行していく。

Filters

revel newを実行したときのアプリケーションのScaffoldの
app/init.go にfiltersが設定されている。

RevelDesign.png
http://revel.github.io/manual/concepts.html

app/init.go
func init() {
  // Filters is the default set of global filters.
  revel.Filters = []revel.Filter{
    revel.PanicFilter,             // Recover from panics and display an error page instead.
    revel.RouterFilter,            // Use the routing table to select the right Action
    revel.FilterConfiguringFilter, // A hook for adding or removing per-Action filters.
    revel.ParamsFilter,            // Parse parameters into Controller.Params.
    revel.SessionFilter,           // Restore and write the session cookie.
    revel.FlashFilter,             // Restore and write the flash cookie.
    revel.ValidationFilter,        // Restore kept validation errors and save new ones from cookie.
    revel.I18nFilter,              // Resolve the requested language
    HeaderFilter,                  // Add some security based headers
    revel.InterceptorFilter,       // Run interceptors around the action.
    revel.CompressFilter,          // Compress the result.
    revel.ActionInvoker,           // Invoke the action.
  }

  // register startup functions with OnAppStart
  // ( order dependent )
  // revel.OnAppStart(InitDB)
  // revel.OnAppStart(FillCache)
}

revel.ActionInvokerがコントローラの特定のアクション実行のトリガー。
最終的にc.Resultにactionの結果が入る(テンプレじゃないよ)

github.com/revel/revel/server.go
func handleInternal(w http.ResponseWriter, r *http.Request, ws *websocket.Conn) {
  var (
    req  = NewRequest(r)
    resp = NewResponse(w)
    c    = NewController(req, resp)
  )
  req.Websocket = ws

  Filters[0](c, Filters[1:]) // ここで各フィルターが実行 & 最後にアクション実行
  if c.Result != nil {
    c.Result.Apply(req, resp)
  } else if c.Response.Status != 0 {
    c.Response.Out.WriteHeader(c.Response.Status)
  }
  // Close the Writer if we can
  if w, ok := resp.Out.(io.Closer); ok {
    w.Close()
  }
}

結果の整形、ジェネレート

  • 返ってくる Resultの型によって、最終的に返すstringを変えている

Resultのインターフェース

type Result interface {
  Apply(req *Request, resp *Response)
}

Resultsの構造体

type ErrorResult struct {
type PlaintextErrorResult struct {
type RenderTemplateResult struct {
type RenderHtmlResult struct {
type RenderJsonResult struct {
type RenderXmlResult struct {
type RenderTextResult struct {
type ContentDisposition string
type BinaryResult struct {
type RedirectToUrlResult struct {
type RedirectToActionResult struct {

RenderTemplateResult の場合

特に何もしないとRenderTemplateResultが利用される。
https://github.com/revel/revel/blob/master/results.go#L165
templateを使ってhtmlを生成している。

それをResponseにしこんで1リクエストの処理が完了する。

話してないこと

  • Routerの挙動
  • watch時の挙動
  • 各Filterの挙動
  • ActionInvoker実行時のBinderについて
  • Interceptors
  • Validation