📝

Rust | axumでモノリシックなWEBサイトを作るための色々

2024/03/09に公開

概要

axumを色々とさわって多少理解できた気がするのでまとめおく。
次の項目をまとめる

  • askamaを使用してのページ表示
  • PostgreSQLを利用する
  • Sessionを利用する
  • 静的コンテンツの利用
  • リダイレクトの方法

askamaを利用したページ表示

モノリシックなアーキテクチャということでページ用のテンプレートを用意。
といっても、公式サンプルに従ってaskamaを利用したので良さげ。

テンプレート内に動的に値を埋め込む場合は、{{ name }}といった感じで埋め込める

PostgreSQLへつなぐ

DBの利用は必須でしょということで、PostgreSQLを使えるようにする。
これも公式サンプルにあるよう、sqlxを利用する。

コードを書く前に、テーブルを用意しておきたいので、sqlx-cliをインストール。

cargo install sqlx-cli

マイグレーションファイルを用意

sqlx migrate add create_table

migrationsディレクトリ配下に、timestamp_create_table.sqlファイルが作成されるので、create tableを用意。

マイグレーションはrunで実行

sqlx migrate run

準備はできたので、コードを用意

Cargo.toml へ下記を追記

sqlx = { version = "0.7", features = ["postgres", "runtime-tokio"] }

とりあえずDBへつなぐところまで

use axum::response::IntoResponse;
use axum::Router;
use axum::routing::get;
use sqlx::postgres::PgPoolOptions;
use tokio::net::TcpListener;

#[tokio::main]
async fn main() {
    let database_url = "postgres://postgres:secret@localhost/localdb";
    let pool = PgPoolOptions::new()
        .connect(&database_url)
        .await
        .expect("DBへの接続に失敗しました。");

    let app = Router::new()
        .route("/", get(index));

    let listener = TcpListener::bind("127.0.0.1:3000").await.unwrap();

    axum::serve(listener, app).await.unwrap();
}

pub async fn index() -> impl IntoResponse {
    return "Hello".to_string();
}

routeでpoolを利用できるようにする
Extensionでpoolを渡すようにする。

Cargo.tomlへ下記を追記

tower = "0.4"
tower-http = {version = "0.5.0", features = ["add-extension"]}

コード部分はこんな感じ

use tower::ServiceBuilder;
use tower_http::add_extension::AddExtensionLayer;
let app = Router::new()
        .route("/", get(index))
        .layer(
            ServiceBuilder::new()
                .layer(AddExtensionLayer::new(pool))
                .into_inner()
        );
pub async fn index(Extension(pool): Extension<Pool<Postgres>>) -> impl IntoResponse {
    return "Hello".to_string();
}

sqlxでのCRUDはサンプルがたくさんあるので略

Sessionを利用する

axum_sessionを利用する。これはサンプルがあまり見つからなかった気がする。
セッションの保持はDBを利用するようにしています。

Cargo.tomlへ下記を追記

axum_session = { version = "0.11", features = ["postgres-rustls"] }

セッション管理用のテーブルは、sessions
※テーブル名は、with_table_nameで指定

use axum_session::{Session, SessionConfig, SessionLayer, SessionPgPool, SessionPgSessionStore};
// セッション
let session_config = SessionConfig::default()
    .with_table_name("sessions");

let session_store =
    SessionPgSessionStore::new(Some(pool.clone().into()), session_config)
        .await.unwrap();

router周り

let app = Router::new()
        .route("/", get(index))
        .layer(SessionLayer::new(session_store))
        .layer(
            ServiceBuilder::new()
                .layer(AddExtensionLayer::new(pool))
                .into_inner()
        );

カウントしてみる

pub async fn index(
    session: Session<SessionPgPool>,
    Extension(pool): Extension<Pool<Postgres>>,
) -> impl IntoResponse {
    let mut count: usize = session.get("count").unwrap_or(0);
    count += 1;

    let message = format!("Hello {}", count);

    session.set("count", count);

    return message.to_string();
}

静的コンテンツの利用

静的ファイルを扱えるようにする。
これも公式のサンプルに従えば良い。

Cargo.toml変更
featuresへfsと追記

tower-http = {version = "0.5.0", features = ["add-extension", "fs"]}

router周り

use tower_http::services::ServeDir;

.nest_service("/assets", ServeDir::new("assets"));が追加の部分

let app = Router::new()
    .route("/", get(index))
    .layer(SessionLayer::new(session_store))
    .layer(
        ServiceBuilder::new()
            .layer(AddExtensionLayer::new(pool))
            .into_inner()
    )
    .nest_service("/assets", ServeDir::new("assets"));

assetsディレクトリを用意して、中に画像等を配置するれば、/assetsで表示できる

リダイレクトの方法

axumで用意されている

pub async fn redirect() -> impl IntoResponse {
    Redirect::permanent("/").into_response()
}

まとめ

とりあえず、表面的な部分をメモってみた。
(Zenn初めましてだけど書きやすいな。)
他にもメモっておきたいこと色々あるので、近い内にまた書こう。

Discussion