はじめに
RustのWebアプリケーションフレームワークとしてRocketが使えそうだなということで、今回は駆け足でRocketを紹介してみます。
前提条件
rustc 1.16.0-nightly (468227129 2017-01-03)
Hello Rocket
まずはHello, world!
を返すシンプルなアプリケーションを動かしてみます。Rocketでは全てnightlyビルドを使って実行します。
$ cargo new --bin hello_rocket
$ cd hello_rocket
$ rustup override set nightly
[dependencies]
rocket = "0.1.4"
rocket_codegen = "0.1.4"
rocket_codegenはget
やroute
を定義するためのカスタムderiveを提供します。
#![feature(plugin)]
#![plugin(rocket_codegen)]
extern crate rocket;
#[get("/")]
fn index() -> &'static str {
"Hello, world!"
}
fn main() {
rocket::ignite().mount("/", routes![index]).launch();
}
実行
$ cargo run
Compiling hello_rocket v0.1.0 (file:///Users/hoge/hello_rocket)
Finished debug [unoptimized + debuginfo] target(s) in 1.16 secs
Running `target/debug/hello_rocket`
🔧 Configured for development.
=> listening: localhost:8000
=> logging: Normal
🛰 Mounting '/':
=> GET /
🚀 Rocket has launched from http://localhost:8000...
GET /:
=> Matched: GET /
=> Outcome: Success
=> Response succeeded.
GET /favicon.ico:
=> Error: No matching routes for GET /favicon.ico.
=> Warning: Responding with 404 Not Found catcher.
=> Response succeeded.
ブラウザでhttp://localhost:8000
にアクセスするとHello world!
が表示されていることが確認できます。
ROCKET_ENV
ROCKET_ENV
環境変数に値をしていすることで、動作環境に応じた制御が可能になります。
$ ROCKET_ENV=stage cargo run
🔧 Configured for staging.
=> listening: 0.0.0.0:80
=> logging: Normal
🛰 Mounting '/':
=> GET /
Error: Failed to start server.
thread 'main' panicked at 'Permission denied (os error 13)', /Users/hoge/.cargo/registry/src/github.com-1ecc6299db9ec823/rocket-0.1.4/src/rocket.rs:474
note: Run with `RUST_BACKTRACE=1` for a backtrace.
ROCKET_ENVにはdevelopment, staging, production が使えます。
上記ではstaging環境でのデフォルトListenポートが80のため権限なしでエラー終了してしまいます。
Rocket.toml
で動作を制御することができます。
[staging]
port = 8000
[production]
port = 8000
設定項目はドキュメントのrocket::configあたりに記載があります。
ルーティング
#[get("/hello/<name>")]
fn hello(name: &str) -> String {
format!("Hello, {}", name)
}
fn main() {
rocket::ignite().mount("/", routes![index, hello]).launch();
}
mount()
の第一引数に指定したパスにroutes!
マクロで指定したメソッドをマウントする形でルーティングを実装します。上記の場合/hello/hoge
にルーティングしますが、mount()
の第一引数に"/super"
を指定すると /hello/hoge
は404となり、/super/hello/hoge
等でルーティングされることになります。
テンプレート
Jinja2およびDjangoテンプレートベースのTeraか、Handlebarsを使うことができます。
私が慣れていることもあるので、Teraを使ってみます。
[dependencies.rocket_contrib]
version = "*"
default-features = false
features = ["tera_templates"]
#![feature(plugin)]
#![plugin(rocket_codegen)]
extern crate rocket;
extern crate rocket_contrib;
use std::collections::HashMap;
use rocket_contrib::Template;
#[get("/hello/<name>")]
fn hello(name: &str) -> Template {
let mut context = HashMap::new();
context.insert("name", name);
Template::render("hello", &context)
}
#[get("/")]
fn index() -> &'static str {
"Hello, world!"
}
fn main() {
rocket::ignite().mount("/", routes![index, hello]).launch();
}
<p>Hello {{name}}</p>
/hello/<name>
の際にテンプレート使った結果を返したいので、templates/hello.tera
を作成します。
テンプレートファイルを格納するディレクトリはデフォルトでtemplates/
ですが、Rocket.toml
から変更することが可能です。
ファイル名はルーティングのメソッド名と一致させておく必要があります。Teraを使いたい場合は拡張子を.tera
に、Handlebarsを使いたい場合は拡張子を.hbs
にします。詳しい説明はドキュメントのテンプレートに記載があります。
JSONを返す
serdeを使ってJSONを扱います。rocket_contrib::JSON
で一段階ラップされています。
#![feature(plugin, proc_macro)]
#![plugin(rocket_codegen)]
extern crate rocket;
extern crate rocket_contrib;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;
use rocket_contrib::JSON;
#[derive(Serialize, Deserialize)]
struct Todo {
id: i32,
title: String,
body: String,
}
#[get("/todo/<id>")]
fn get_todo(id: i32) -> JSON<Todo> {
JSON(Todo {
id: id,
title: "todo title 1".to_string(),
body: "todo body 1".to_string(),
})
}
fn main() {
rocket::ignite().mount("/", routes![index, hello, get_todo]).catch(errors![not_found]).launch();
}
/todo/1
にアクセスすると{"id":1,"title":"todo title 1","body":"todo body 1"}
を取得することができます。
エラーハンドリング
use rocket::Request;
#[error(404)]
fn not_found(req: &Request) -> String {
format!("not found {}", req)
}
fn main() {
rocket::ignite().mount("/", routes![index, hello]).catch(errors![not_found]).launch();
}
http://localhost:8000/hell
にアクセスするとnot found GET /hell
が表示されます。
データベースアクセス
上記にDiesel SQLiteを使った例があります。
DieselはPostgreSQLおよびSQLiteしかサポートしていませんが、上記のコード例を見る限りはrust-mysql-simpleを使ってデータベースアクセスすることはできそうな気がします。
DieselもそろそろMySQLサポートされるような気配もしますし。。。
最後に
駆け足でRocketについて説明しました。
豊富なコード例やドキュメントの充実具合、テストライブラリ完備、将来Websocketのサポートが予定されていたりして、今後Rustで使われる予感がしています。Rustを学ぶ第一歩としてRocketを使ってみてはいかがでしょうか。