GoのウェブフレームワークRevelを試してみる

Go製のウェブフレームワーク[Revel](http://revel.github.io/)を試してみる。

# サンプルを動かしてみる

```bash
$ go get github.com/revel/cmd/revel
$ revel run github.com/revel/revel/samples/chat
~
~ revel! http://revel.github.io
~
2014/08/02 22:18:17 revel.go:320: Loaded module testrunner
2014/08/02 22:18:17 revel.go:320: Loaded module static
2014/08/02 22:18:17 run.go:57: Running chat (github.com/revel/revel/samples/chat) in dev mode
2014/08/02 22:18:17 harness.go:165: Listening on :9000
```

`http://localhost:9000`でアクセス出来る。

# スケルトンを作成してみる

```bash
$ revel new board # $GOPATH # $GOPATH/src/board にスケルトンアプリケーションが作成される
```

`revel run board` でサーバが起動する。

# ディレクトリ構成

```bash
$ tree
.
├── app
│   ├── controllers
│   │   └── app.go
│   ├── init.go
│   ├── routes
│   │   └── routes.go
│   ├── tmp
│   │   └── main.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
```

# 各種機能概要

## Routingの設定


`conf/routes` に必要なルーティングを設定する

```
# conf/routes
# This file defines all application routes (Higher priority routes first)

module:jobs                                          # Import all routes from the jobs module

GET    /login                 App.Login              # A simple path
GET    /hotels/               Hotels.Index           # Match /hotels and /hotels/ (optional trailing slash)
GET    /hotels/:id            Hotels.Show            # Extract a URI argument
WS     /hotels/:id/feed       Hotels.Feed            # WebSockets.
POST   /hotels/:id/:action    Hotels.:action         # Automatically route some actions.
GET    /public/*filepath      Static.Serve("public") # Map /app/public resources under /public/...
*      /debug/                module:testrunner      # Prefix all routes in the testrunner module with /debug/
*      /:controller/:action   :controller.:action    # Catch all; Automatic URL generation

```


例えば `/hello` にアクセスした時にページを表示させたい場合は


```
GET /hello Hello.Index
```
と書く。

## Controllers

`app/controllers/` 以下にcontroller用のファイルを置く

```go:app/controllers/hello.go
package controllers

import "github.com/revel/revel"

type Hello struct {
  *revel.Controller
}

func (h Hello) Index() revel.Result {
  return h.Render()
}
```

## Templates

`app/views/`以下にいわゆるViewファイルを置いていく。
デフォルトでは、`app/views/#{controller名}/#{action名}.html`になる。

```html:app/views/Hello/Index.html
{{set . "title" "Hello"}}
{{template "header.html" .}}

<header class="hero-unit" style="background-color:#A9F16C">
  <div class="container">
    <div class="row">
      <div class="hero-text">
        <h1>Hello World!</h1>
        <p></p>
      </div>
    </div>
  </div>
</header>

<div class="container">
  <div class="row">
    <div class="span6">
      {{template "flash.html" .}}
    </div>
  </div>
</div>

{{template "footer.html" .}}
```

この時点で、`$ go run board` でサーバー起動し
`http://localhost:9000/hello`にアクセスすれば上のページが表示される。

## Interceptors

`Interceptors`はcontrollerメソッドの前後やpanic時、
複数のcontrollerからアクセスがある場合に利用する。

```go:app/controllers/init.go
package controllers

import (
  "fmt"

  "github.com/revel/revel"
)

type Intercepter struct {
}

func (i *Intercepter) Before() revel.Result { // 返り値は `revel.Result`
  fmt.Println("Before Method")
  return nil
}

func (i *Intercepter) After() revel.Result {
  fmt.Println("After Method")
  return nil
}

func (i *Intercepter) Panic() revel.Result {
  fmt.Println("Panic Method")
  return nil
}

func init() {
  revel.InterceptMethod((*Intercepter).Before, revel.BEFORE) // コントローラ実行前
  revel.InterceptMethod((*Intercepter).After, revel.AFTER)       // コントローラ実行後
  revel.InterceptMethod((*Intercepter).Panic, revel.PANIC)      // panic実行後     
}
```

設定はControllerで行う。

```go:app/controllers/app.go
package controllers

import "github.com/revel/revel"

type App struct {
  *revel.Controller
  Intercepter // 構造体に追加
}

func (c App) Index() revel.Result {
  return c.Render()
}
```

## Filters

revelのフィルターは、アプリケーションのミドルウェアのことを指す。
`revel new`で作った場合いくつか`app/init.go`に書かれている

```go:app/init.go
  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.
  }
```


以下の図のFilters部分をここで定義していく

![RevelDesign.png](https://qiita-image-store.s3.amazonaws.com/0/3163/2cce09fe-dcae-9852-b0a1-b5d5f71def27.png)
http://revel.github.io/manual/concepts.html


例えば、一つ目のPanicFilterはこんなように書かれている。

```go:github.com/revel/revel/panic.go
// PanicFilter wraps the action invocation in a protective defer blanket that
// converts panics into 500 error pages.
func PanicFilter(c *Controller, fc []Filter) {
  defer func() {
    if err := recover(); err != nil {
      handleInvocationPanic(c, err)
    }
  }()
  fc[0](c, fc[1:])
}
```

Filterの定義は `type Filter func(c *Controller, filterChain []Filter)`
最後に `fc[0](c, fc[1:])`で次のフィルターに実行を移している。

(続きます)