Twitterを支えるfinatraを使ってみた

ChainZ(クリエイター)
いろいろやってます。

finatra

finatra(https://twitter.github.io/finatra/)はSinatraからアイデアを得て、twitterSeverを使って作られたScalaベースのwebフレームワークです。この記事を書いた時点で、最新のバージョンは2.0です。

Hello World

finatraのリポジトリ(https://github.com/twitter/finatra)にあるexamples/finatra-hello-worldを使います。 README.mdには既に詳しいディプロイ方法が載ってありますが、起動方法は下記のコマンドを叩く:

1
./sbt -Dlogback.configurationFile=src/test/resources/logback-test.xml run

portを変える
このプロジェクトはデフォルトに8888(フロント)と9990(内部用)を使っていますが、もしいずれかのportは既に他のアプリに使われたりしたら、エラーがでます。portを変えるには、runの後ろに、-http.port=:1212(フロント)、-admin.port=:9911(内部用)というオプションを追加してください。

ブラウザを開いて、http://localhost:8888/hi?name=Worldにアクセスすると

ServerとControllerをつくてみる

先ほどのHello Worldプロジェクトでは、既にHelloWorldSeverHelloWorldControllerが 用意されたので、特に何もしなくてもサイトとして動きます。これから、自分でServerとControllerを作ってみます:

Server

./src/main/scala/jp/co/befool/biiBiiServer.scalaを作成します。

この記事のコードがパッケージjp.co.befool.biiにあります。パッケージというコンセプトが分かる方は、自分好きなパッケージにしても大丈夫です。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package jp.co.befool.bii

import com.twitter.finatra.http.HttpServer
import com.twitter.finatra.http.routing.HttpRouter
import com.twitter.finatra.http.filters.CommonFilters

// finatraのHttpServerを拡張
class BiiServer extends HttpServer {
  // routerなど設定
  override def configureHttp(router: HttpRouter) {
    router
      .filter[CommonFilters] // Commonフィルターが必要です
  }
}

// BiiServerのエントリーポイント
object BiiServerMain extends BiiServer

Controller

./src/main/scala/jp/co/befool/bii/controller/RootController.scalaを作成します:

1
2
3
4
5
6
7
8
9
10
11
12
package jp.co.befool.bii.controller

import com.twitter.finatra.http.Controller
import com.twitter.finagle.http.Request

// finatraのControllerを拡張
class RootController extends Controller{
  // http://localhost
  get("/") { request: Request =>
    response.ok.body(s"<h1>HelloWorld</h1>")
  }
}

BiiServerからRootControllerにルートします:

1
2
3
4
5
6
7
8
9
10
11
import jp.co.befool.bii.controller.RootController

// finatraのHttpServerを拡張
class BiiServer extends HttpServer {
  // routerなど設定
  override def configureHttp(router: HttpRouter) {
    router
      .filter[CommonFilters] // Commonフィルターが必要です
       .add[RootController] // RootControllerを追加
  }
}

コマンドsbt runすると

1
2
3
4
5
6
7
8
[warn] Multiple main classes detected.  Run 'show discoveredMainClasses' to see the list

Multiple main classes detected, select one to run:

 [1] com.twitter.hello.HelloWorldServerMain
 [2] jp.co.befool.bii.BiiServerMain

Enter number:

サーバーが二つ検出されたので、どっちを実行するかという質問です。2で入力します。問題なければ、ブラウザでhttp://localhost:8888で見ると:

サーバーちゃんと動いています。

RequestのValidationをやってみます

先ほど作ったRootControllerの直前に、RootRequestを作成します:

1
2
3
4
5
6
7
8
import com.twitter.finatra.request.QueryParam
import com.twitter.finatra.validation.NotEmpty

case class RootRequest (
  // @NotEmpty記号は空値が不可ということです
  // @QueryParamは@NotEmptyの対象パラメータ名です
  @NotEmpty @QueryParam name: String
)

RootControllerget("/")ルートに修正を入れます:

1
2
3
4
5
get("/") { request: RootRequest =>
  response.ok
    .header("Content-Type", "text/html; charset=utf-8")
    .body(s"<h1>こんにちは、${request.name}</h1>")
}

requestのタイプを先ほど作ったRootRequestに宣言します。

sbt runして、ブラウザでhttp://localhost:8888にアクセスして、下記のエラーが表示されます:

URLをhttp://localhost:8888/?name=世界にして、アクセスすると:

Requestのバリデーション動いてるとわかりますね。

まとめ

finatraに興味ある方は、より詳しい説明と使い方が公式のリポジトリに載っています:https://github.com/twitter/finatra/blob/master/http/README.md

オープンソースのwebフレームワークとしては、finatraのドキュメントなどはまだ不足してる感じがします。しかし、twitterという超大手なweb企業が使ってるというとこが、魅力を感じる人もいるでしょう(自分はそうでした)。次回はfinatraを利用してアプリのバックエンドを作ってみようと思います。