Go

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

More than 3 years have passed since last update.

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

サンプルを動かしてみる

$ 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でアクセス出来る。

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

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

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

ディレクトリ構成

$ 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用のファイルを置く

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になる。

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からアクセスがある場合に利用する。

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で行う。

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に書かれている

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
http://revel.github.io/manual/concepts.html

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

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:])で次のフィルターに実行を移している。

(続きます)