
はじめに
Rust の Web アプリケーションフレームワークとしては actix-web rocket がメジャーで、 tide warp がそれに続くといったところが現在の状況でしょうか。
ここでは Tide について簡単に見ていくことにします。
Tide とは
Tide は Rust によるシンプルな Web アプリケーションフレームワークです。
以下のように説明されています。
Tide は、迅速な開発のために作られた、ミニマルで実用的な Rust の Web アプリケーションフレームワークです。非同期のWebアプリケーションやAPIの構築をより簡単に、より楽しくするための堅牢な機能を備えています。
初回コミットは2018年8月とまだ若いプロジェクトではあります。
async-std を基盤とした非同期Webアプリケーション という点がポイントになるかと思います。
プロジェクトの作成
cargo でプロジェクトを作成します。
$ cargo new tide-example --bin
IDEA で Command Line Launcher を設定しており、Rust プラグインが入っている場合は、以下でプロジェクトを開くことができます。
$ idea tide-example/Cargo.toml
Command Line Launcher については以下を参照してください。
Cargo.toml を開き、依存を以下のように追加します。
[dependencies]
tide = "0.16.0"
async-std = { version = "1.9.0", features = ["attributes"] }
async-std は非同期処理のランタイムを提供します。
Hello World
main.rs を以下のように編集します。
#[async_std::main] async fn main() -> tide::Result<()> { let mut app = tide::new(); app.at("/").get(|_| async { Ok("Hello, world!") }); app.listen("127.0.0.1:8080").await?; Ok(()) }
#[async_std::main] は、先程導入した async-std の attributes で利用できるアトリビュートです。
これにより、続く関数が非同期処理のランタイム上で動きます。
関数内では、最初に tide::new() で tide のオブジェクトを作成しています。
続いて tide.at() にてURLのパスを指定し、HTTP GET に応答する処理をクロージャで定義しています。
async の非同期ブロックで Result を返す形となります。
最後に listen() で指定ポートでリスンを開始しています。
await でサーバの完了を待機します。
実行してみましょう。
$ cargo run
http://localhost:8080/ にアクセスすれば、以下のような結果が得られます。

Response::builder を使って自由にレスポンスを作成することもできます。
HTMLを返却するよう、以下のように書き換えてみましょう。
app.at("/").get(|_| async { Ok(tide::Response::builder(200) .body("<html><h2>Hello, world!</h2></html>") .header("Server", "tide") .content_type(tide::http::mime::HTML) .build()) });
以下のような結果が得られます。

パスパラメータを受け取る
url として :user のように名前をつけることで、req.param("user") のようにパラメータを取得することができます。
先程の例をパラメータを受け取るように変更します。
app.at("/:user").get(|req: tide::Request<()>| async move { Ok(tide::Response::builder(200) .body(format!("<html><h2>Hello, {}!</h2></html>", req.param("user")?)) .header("Server", "tide") .content_type(tide::http::mime::HTML) .build()) });
http://localhost:8080/Thom にアクセスすれば、以下のような結果が得られます。

ここでは直接HTMLを返していますが、以下のように外部の静的なファイルを扱うこともできます。
app.at("/src/*").serve_dir("src/")?; app.at("/index").serve_file("index.html")?;
JSON を返す
レスポンスに JSON を返すには json! マクロが用意されています。
マクロをパスに追加します。
use tide::prelude::json;
json! マクロにより以下のようにJSONのレスポンスを返すことができます。
app.at("/animals").get(|_| async { Ok(json!([ { "name": "chashu", "age": 8 }, { "name": "nori", "age": 3 } ])) });
http://localhost:8080/animals にアクセスすれば、以下のような結果が得られます。

serde を使ってシリアライズした結果として返すこともできます。依存に追加します。
[dependencies]
...
serde = { version = "1.0", features = ["derive"] }
ソースを以下のように変更します。
use tide::prelude::json; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize)] struct Animal { name: String, age: u8, } #[async_std::main] async fn main() -> tide::Result<()> { let mut app = tide::new(); app.at("/animals").get(|_| async { let animals = vec![ Animal { name: "chashu".into(), age: 8 }, Animal { name: "nori".into(), age: 3 }, ]; Ok(tide::Body::from_json(&animals)?) }); app.listen("127.0.0.1:8080").await?; Ok(()) }
Ok(json!(animals)) と json! マクロを使うこともできます。
http://localhost:8080/animals にアクセスすれば、先程と同様の結果が得られます。

クエリパラメータを受け取る
クエリパラメータは serde を経由して Request.query() で簡単にインスタンスを取得することができます。
app.at("/animal").get(|req: tide::Request<()>| async move { let animal: Animal = req.query()?; Ok(tide::Body::from_json(&animal)?) });
http://localhost:8080/animal?name=chashu&age=8 にアクセスすれば、クエリパラメータから構築された JSON を得ることができます。

POSTパラメータを受け取る
ポストパラメータを受け取る場合も同じような構成で可能です。
JSON をリクエストボディとして受け取る場合は以下のようにします。
app.at("/animal").post(|mut req: tide::Request<()>| async move { let animal: Animal = req.body_json().await?; Ok(tide::Body::from_json(&animal)?) });
curl で JSON を POST してみましょう。
$ curl -X POST -H "Content-Type: application/json" -d '{"name":"chashu","age":8}' localhost:8080/animal
{"name":"chashu","age":8}
POST した内容がエコーされていますね。