Rustのaxumで利用できるテンプレートエンジンは幾つかある
handlerbars: JavaScriptのHandler.jsにインスパイアされて開発されたRust用のテンプレートエンジン
minijinja: PythonのJinja2の文法を採用して作られている。
Tera: Djangoのテンプレートエンジンなどの影響を強く受けたもので、フィルターやマクロなど非常に豊富な機能を持っている
### Teraの準備
Cargo.tomlのdependenciesに以下を追加
axum-template="0.14.0" tera="1.17.1"
### テンプレート作成
template/index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>{{title}}</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.css" rel="stylesheet" crossorigin="anonymous"> </head> <body class="container"> <h1 class="display-6 my-2">{{title}}</h1> <p class="my-2">{{message}}</p> </body> </html>
### axumからTeraを利用
Teraのテンプレートパスを指定してTeraのインスタンス作成
テンプレートエンジンとの間でやりとりするデータの管理はContextを使用する
レンダリングの実行はtera.render, htmlインスタンスの返却はaxum::response::Html
#[tokio::main] async fn main() { let app = axum::Router::new() .route("/", axum::routing::get(handle_index)); axum::Server::bind(&"192.168.56.10:8000".parse().unwrap()) .serve(app.into_make_service()) .await .unwrap(); } async fn handle_index()-> axum::response::Html<String> { let tera = tera::Tera::new("template/*").unwrap(); let mut context = tera::Context::new(); context.insert("title", "Index page"); context.insert("message", "これはサンプルです。"); let output = tera.render("index.html", &context); axum::response::Html(output.unwrap()) }
### フォームの送信
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>{{title}}</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.css" rel="stylesheet" crossorigin="anonymous"> </head> <body class="container"> <h1 class="display-6 my-2">{{title}}</h1> <div class="alert alert-primary"> <p class="my-2">{{message}}</p> </div> <form method="post" action="/post"> <div class="mb-3"> <label for="name" class="form-label"> Your name:</label> <input type="text" class="form-control" name="name" id="name"> </div> <div class="mb-3"> <label for="mail" class="form-label"> Email address:</label> <input type="text" class="form-control" name="mail" id="mail"> </div> <input type="submit" class="btn btn-primary" value="Submit"> </form> </body> </html>
/postのルーティングを用意する
use serde::{Serialize, Deserialize}; #[derive(Serialize, Deserialize)] struct Myform { name: String, mail: String, } #[tokio::main] async fn main() { let app = axum::Router::new() .route("/", axum::routing::get(handle_index)) .route("/post", axum::routing::post(handle_post)); axum::Server::bind(&"192.168.56.10:8000".parse().unwrap()) .serve(app.into_make_service()) .await .unwrap(); } // 省略 async fn handle_post(axum::Form(myform): axum::Form<Myform>) -> axum::response::Html<String> { let msg = format!("I am {}<{}>.", myform.name, myform.mail); let tera = tera::Tera::new("template/*").unwrap(); let mut context = tera::Context::new(); context.insert("title", "Index page"); context.insert("message", &msg); let output = tera.render("index.html", &context); axum::response::Html(output.unwrap()) }