🌙

axumを静的ウェブサイトホストサーバーにするための基本形メモ (Rust)

2024/09/17に公開


ウェブサイトのバックエンドはRustで構築したいと思っており、何となくaxumを使いたいと思っていました。ただ、具体的にaxumは一体何を担うのか等よく理解できておらず、公式の"Hello World"のコードで微妙にわかった気がしても、静的ウェブサイトホスティングとしてはどうなのかがわからないままでした。例としても公式のexamples以外にあまり見当たらず、その例も後から見ると色々な内容が載っており親切なのですが、最初の私にとっては逆に最小構成が埋もれておりパッと見では全体像が見えませんでした。ある程度把握するのに若干苦労したので備忘として残しておきます。おまけでWebAPIサーバーの例も載せています。
[axum 0.7.5]

前提

Cargo.toml
[dependencies]
axum = { version = "0.7.5", features = ["http2"] }
tokio = { version = "1.40.0", features = ["full"] }
tower-http = { version ="0.5.2", features = ["fs"] }
dir tree & static files
project/
├── Cargo.toml
├── src/
│   └── main.rs
└── static/
    ├── index.html
    └── css/
        └── style.css
index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Hello World</title>
  <link rel="stylesheet" href="./css/style.css" type="text/css">
</head>
<body>
  <h1>Hello World</h1>
</body>
</html>
style.css
h1 {
  color: #f00;
}

静的ファイルホスティング

最小構成

use tower_http::services::ServeDir;
use axum::Router;
use tokio::net::TcpListener;

#[tokio::main]
async fn main() {
    // set path of contents files directory as service
    let serve_dir = ServeDir::new("./static");
    // set router path of the service
    let app = Router::new().nest_service("/", serve_dir);
    // get TCP listener of requests for port 3000
    let listener = TcpListener::bind("0.0.0.0:3000").await.unwrap();
    // starting server
    axum::serve(listener, app).await.unwrap();
}

ルーティングの注意点

ルーティングパスを変えるとHTML内のリンクパスの記述も変化させる必要があるため注意する。

  • 動作しない例
main.rs
Router::new().nest_service("/static", ServeDir::new("./static"))
index.html
<link rel="stylesheet" href="./css/style.css" type="text/css">
  • 動作する例
main.rs
Router::new().nest_service("/static", ServeDir::new("./static"))
index.html
<link rel="stylesheet" href="./static/css/style.css" type="text/css">

WebAPIサーバー

最小構成+

use axum::{Router, routing::get, Json};
use tokio::net::TcpListener;

#[tokio::main]
async fn main() {
    // set router path with response handler for GET request as service
    let app = Router::new().route("/", get(hello));
    // get TCP listener of requests for port 3000
    let listener = TcpListener::bind("0.0.0.0:3000").await.unwrap();
    // starting server
    axum::serve(listener, app).await.unwrap();
}
// return string as Content-Type: application/json
async fn hello() -> Json<&'static str> {
    Json(r#"{"greeting": "Hello World"}"#)
}

axumの個人的な理解

  • towerが取り扱うのはあるリクエストに対するレスポンスの定義
    • Serviceトレイトは簡単にはasync fn(Request) -> Result<Response, Error>らしい
  • hypertokio前提のHTTPのライブラリ
    • tokioのグリーンスレッドを用いたマルチスレッディング処理
    • バッファ操作のラッピング
    • 新しいコネクションをポートでリッスン 等
  • axumhypertowerをうまく組み合わせるための枠組み
    • そのためusetowerが出てくる
    • hyperaxumに内臓されているためuseには出てこない

所感

axumを理解していくための基礎知識が得られたと思います。

参考文献

Discussion