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())
}