🦀

100日後にRustをちょっと知ってる人になる: [Day 36]はじめての Web フレームワーク

2022/10/01に公開

Day 36 のテーマ

Day 35 では、非同期処理の動きをみようと思い Web サーバを作ってみました。昨日の時点ではシングルスレッドの Web サーバでしたが公式ドキュメントの方では、あの実装からスレッドプールを実装しマルチスレッドにしたサーバの説明が行われていました。

このドキュメントに沿えばマルチスレッド化できます。コードの説明は丁寧に行われれているのですが、コンパイルエラーを見ながら、コードを実装していくという流れになっているので少し読みにくいかもしれませんけれど。

さて、Web サーバを作ってみたりしましたが、実際は自分で Web サーバを書き上げることはきっとしないですよね。車輪の再発明のようなことになりますよね。
例えば、Java のアプリケーションを思い浮かべてみると、サーバ用コンポーネントになる Apache Tomcat を自分で実装する人はいないですよね。多くの人は Web フレームワークの Spring Boot を使って組み込み Tomcat を使ったりする人が多いのではないでしょうか。

Java と同様に Rust にも Web フレームワークがあります。ただ、Java の Spring のようなデファクトと言えるものはまだ確立されていないように見えます。ということで、ぼくの観測範囲で見つけている Rust の Web フレームワークを紹介します。

Rust Web フレームワーク

アクティブなプロジェクトや、非アクティブなプロジェクトもあると思いますが次のようなフレームワークがあります。
(全量の中からはまだまだ1部分のはず)

名称 公式サイト GitHubリポジトリ ドキュメント
actix-web https://actix.rs/ https://github.com/actix/actix-web https://docs.rs/actix-web/latest/actix_web/
rocket https://rocket.rs/ https://github.com/SergioBenitez/rocket https://rocket.rs/v0.5-rc/guide/introduction/
warp - https://github.com/seanmonstar/warp https://docs.rs/warp/
gotham http://gotham.rs/ https://github.com/gotham-rs/gotham/ https://docs.rs/gotham/
Thruster - https://github.com/trezm/Thruster https://docs.rs/thruster
Tide - https://github.com/rustasync/tide https://docs.rs/tide
salvo - https://github.com/salvo-rs/salvo https://docs.rs/salvo/
trillium https://trillium.rs/ https://github.com/trillium-rs/trillium https://docs.trillium.rs/
axum - https://github.com/tokio-rs/axum https://docs.rs/axum
Poem - https://github.com/poem-web/poem https://github.com/poem-web/poem/blob/master/poem/README.md
Viz https://viz.rs/ https://github.com/viz-rs/viz https://docs.rs/viz/

この中から1つだけ今日はためしてみます。

はじめての Rocket

Rocket を試してみようと思います。

Dependency

rocket最新バージョン0.5.0-rc.2 なので、次のように Cargo.toml に定義します。

[dependencies]
rocket = "0.5.0-rc.2"

Crate

rocket が使えるように次のクレートを宣言しておきます。

#[macro_use] extern crate rocket;

Rocket の動作概要

Rocket は、暗いアウトからのリクエストを受け付け、処理を実施した後にクライアントにレスポンスを返す Web フレームワークです。以下のステップをライフサイクルとして動作します。

  • ルーティング
    • クライアントからのリクエストを解析し、宣言されたルート属性と照らし合わせて要求ハンドラを決定します。
  • 検証
    • リクエストの検証をおこないます。検証が失敗した場合、リクエストを次のルートに転送するか、エラーハンドラを呼びだします。
  • 処理
    • ルートに関連付けられたリクエストハンドラを呼び出します。
  • 応答
    • 適切な応答を生成してクライアントに送信します。

ルーティング

ルートハンドラの定義を行います。

ルートは、リクエストと照合するためのパラメータセットです。
ハンドラは、任意の引数を受け取り、任意の型を返す関数です。

照合するパラメータには以下のようなものがあります:

  • 静的パス
  • 動的パス
  • パスセグメント
  • フォーム
  • クエリー文字列
  • リクエストフォーマット指定子
  • ボディデータ
    など
#[get("/hello")]        // <----- ルート
fn index() -> &'static str { //  <----- ハンドラ
    "Hello, Rocket"
}

ここでは、#[get] を用いて GET リクエストを受け取っています。もちろんそれ以外の HTTP メソッドも対応しているので、#[post], #[put] なども使用可能です。

ルートのマウント

リクエストをディスパッチする前に、ルートのマウントを行います。
mount メソッドでは次の処理を行います:

  • ベースパスの設定
    • 次の例では //test
  • ルートとベースパスの関連付けを routes! マクロで実施
    • 次の例では index ルートを設定
#[launch]
fn rocket() -> _ {
    rocket::build()
        .mount("/", routes![index])
        .mount("/test", routes![index])
}

以上で実装は終了です。雰囲気的には、よくある Web フレームワークの実装に似ていますよね。

起動

Rocket は起動すると、非同期のマルチスレッドサーバを起動します。そして、リクエストにマッチするルートにディスパッチを行い、リクエスト処理を行います。

起動するには 2 つの方法があります。

  • #[launch] 属性による起動 (推奨)
  • #[rocket::main] 属性による起動

#[launch]

#[launch]
fn rocket() -> _ {
    rocket::build()
        .mount("/", routes![index])
        .mount("/test", routes![index])
}

#[rocket::main]

launch() が返す Future へのハンドルが欲しいときや、launch() の戻り値を検査したいときに #[rocket::main] が役に立ちます。

#[rocket::main]
async fn main() -> Result<(), rocket::Error> {
    let _rocket = rocket::build()
        .mount("/", routes![index])
        .mount("/test", routes![index])
        .launch()
        .await?;
    Ok(())
}

Day 36 のまとめ

Rust で使用する Web フレームワークについて調べてみました。その中から Rocket によるサンプルの実装を確認してみました。その他言語にあるような Web フレームワーク同様に非常にシンプルに マルチスレッド 非同期サーバの起動とそれを用いた Web アプリケーションを作成することができました。

GitHubで編集を提案

Discussion