Go製フレームワークRevelの処理の流れです。
ざっくりとした処理の流れの話です。
以下、社内勉強会用資料。
revelの主な処理の流れ
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.go
と app/tmp/main.go
にファイルが作られている。
.
├── app
│ ├── controllers
│ │ └── app.go
│ ├── init.go
│ ├── routes # ここと
│ │ └── routes.go
│ ├── tmp # ここが新しい
│ │ └── main.go
│ └── views
# 省略
revel の内部構造
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 内でビルドしている.
- app/tmp, app/routes/ のクリーンアップ
- アプリケーションに必要な情報の収集(不必要なものを除外)
https://github.com/revel/revel/blob/master/harness/build.go#L40 - db.import が設定されている場合は、それの実行
- main.go と routes.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を実行
// 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.go
にfunc Run()
があり、なんだかんだでそれが走る。
リクエストを受け付けると handlerが実行され
revel.Controllerとrevel.Request, revel.Responseが作成されて
各Filtersを実行していく。
Filters
revel new
を実行したときのアプリケーションのScaffoldの
app/init.go
にfiltersが設定されている。
http://revel.github.io/manual/concepts.html
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の結果が入る(テンプレじゃないよ)
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