LoginSignup
47
42

More than 5 years have passed since last update.

RustのRocketでWebアプリケーションを作ってみる

Posted at

はじめに

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
Cargo.toml
[dependencies]
rocket = "0.1.4"
rocket_codegen = "0.1.4"

rocket_codegenはgetrouteを定義するためのカスタムderiveを提供します。

src/main.rs
#![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で動作を制御することができます。

Rocket.toml
[staging]
port = 8000

[production]
port = 8000

設定項目はドキュメントのrocket::configあたりに記載があります。

ルーティング

src/main.rs
#[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を使ってみます。

Cargo.toml
[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();
}
templates/hello.tera
<p>Hello {{name}}</p>

/hello/<name>の際にテンプレート使った結果を返したいので、templates/hello.teraを作成します。

テンプレートファイルを格納するディレクトリはデフォルトでtemplates/ですが、Rocket.tomlから変更することが可能です。
ファイル名はルーティングのメソッド名と一致させておく必要があります。Teraを使いたい場合は拡張子を.teraに、Handlebarsを使いたい場合は拡張子を.hbsにします。詳しい説明はドキュメントのテンプレートに記載があります。

JSONを返す

serdeを使ってJSONを扱います。rocket_contrib::JSONで一段階ラップされています。

src/main.rs
#![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"}を取得することができます。

エラーハンドリング

src/main.rs
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を使ってみてはいかがでしょうか。

47
42
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
47
42