Rust | axumでモノリシックなWEBサイトを作るための色々
概要
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