axum使用minijinja.md
构建流程
- 创建模板环境
let mut env = Environment::new();
- 注册模板
env.add_template(name: &str, source: &str)- name 是模板名,你自定义即可
- source 是模板内容
env.add_template("index.html", "<h1>{{ name }}</h1>").unwrap();
- 获取模板
let tmpl = env.get_template(name: &str).unwrap();- 这个name需要和注册的name一致
let tmpl = env.get_template("index.html").unwrap();
- 渲染模板
let html = tmpl.render(context! {name => "axum"}).unwrap();- 这里是把变量传进模板,最终得到渲染后的字符串
use minijinja::{Environment, context};
fn main() {
let mut env = Environment::new();
env.add_template("hello.html", "<h1>Hello {{ name }}</h1>")
.unwrap();
let tmpl = env.get_template("hello.html").unwrap();
let html = tmpl
.render(context! {
name => "axum"
})
.unwrap();
println!("{}", html);
}
渲染html
可以手动读取文件内容,把内容放入source中
- 在异步任务中,使用
tokio的fs读取文件内容
// 读取文件内容(阻塞读取的)
let source = std::fs::read_to_string("templates/index.html").unwrap();
// 异步读取
let source = tokio::fs::read_to_string("templates/index.html").await?;
// 加入到模板中
env.add_template("index.html", &source).unwrap();
手动html字符串
use axum::{
Router,
routing::get,
response::Html
};
use tokio::net::TcpListener;
use minijinja::{Environment, context};
async fn a() -> Html<String> {
let mut env = Environment::new();
env.add_template(
"index.html",
r#"
<html>
<head>
<link rel="stylesheet" href="/static/app.css">
</head>
<body>
<h1>{{ title }}</h1>
</body>
</html>
"#,
).unwrap();
let tmpl = env.get_template("index.html").unwrap();
let html = tmpl.render(context! {
title => "Hello minijinja"
}).unwrap();
Html(html)
}
#[tokio::main]
async fn main() {
let app = Router::new()
.route("/a", get(a));
let listener = TcpListener::bind("0.0.0.0:9090")
.await
.expect("bind failed");
println!("Server running at http://0.0.0.0:9090");
axum::serve(listener, app).await.unwrap();
}
html文件读取数据
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h2>{{ title }}</h2>
</body>
</html>
use axum::{Router, response::Html, routing::get};
use tokio::net::TcpListener;
use minijinja::{Environment, context};
async fn a() -> Html<String> {
// 获取html内容
let html_str = tokio::fs::read_to_string("static/a.html").await.expect("sss");
// 创建Environment
let mut env = Environment::new();
// 注册模版信息
env.add_template("index.html", &html_str).unwrap();
// 获取模版信息
let tmpl = env.get_template("index.html").unwrap();
// 渲染模板
let html = tmpl
.render(context! {
title => "Hello minijinja"
})
.unwrap();
Html(html)
}
#[tokio::main]
async fn main() {
let app = Router::new().route("/a", get(a));
let listener = TcpListener::bind("0.0.0.0:9090")
.await
.expect("bind failed");
println!("Server running at http://0.0.0.0:9090");
axum::serve(listener, app).await.unwrap();
}
各种类型用法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>str</h1>
<p>{{ title }}</p>
<h1>数字</h1>
<p>{{ age }}</p>
{% if age < 18 %}
<p>未成年</p>
{% else %}
<p>成年</p>
{% endif %}
<h1>浮点数</h1>
<p>{{ score }}</p>
<h1>布尔值</h1>
<p>{{ vip }}</p>
{% if vip %}
<p>vip用户</p>
{% else %}
<p>普通用户</p>
{% endif %}
<h1>vec</h1>
<ul>
{% for i in tags %}
<li>{{ i }}</li>
{% endfor %}
</ul>
<h1>hashmap</h1>
<!-- hashmap.key 或 hashmap["key"] 获取-->
<p>apple: {{ hash_map.apple }}</p>
<p>gugu: {{ hash_map["gugu"] }}</p>
<!-- 没有的值会显示<p></p> -->
<p>{{ hash_map["ccc"] }}</p>
<!-- 设置默认值 -->
<p>ccc: {{ hash_map["ccc"]|default("没有这个值") }}</p>
{% for k,v in hash_map | items %}
<p>{{k}} - {{v}}</p>
{% endfor %}
<h1>enum</h1>
<!-- serde可以控制输出内容 -->
<p>{{ status }}</p>
<p>{{ status1 }}</p>
<h1>struct</h1>
<p>{{ user.name }}</p>
<p>{{ user.age }}</p>
{% if user.hooby %}
<ul>
{% for h in user.hooby %}
<li>{{ h }}</li>
{% endfor %}
</ul>
{% endif %}
</body>
</html>
use axum::{Router, response::Html, routing::get};
use tokio::net::TcpListener;
use tower_http::services;
use std::collections::HashMap;
use minijinja::{Environment, context};
use serde::Serialize;
// 需要实现serde::Serialize,context!才能转换
#[derive(Debug, Serialize)]
struct User {
name: String,
age: u8,
hooby: Option<Vec<String>>
}
// 需要实现serde::Serialize,context!才能转换
#[derive(Serialize)]
enum Status {
Success,
Failed,
#[serde(rename(serialize = "ppp"))]
Pending,
}
async fn a() -> Html<String> {
// 获取html内容
let html_str = tokio::fs::read_to_string("static/a.html").await.expect("sss");
// 创建Environment
let mut env = Environment::new();
// 注册模版信息
env.add_template("index.html", &html_str).unwrap();
// 获取模版信息
let tmpl = env.get_template("index.html").unwrap();
let mut hash_map = HashMap::new();
hash_map.insert("apple", 3);
hash_map.insert("gugu", 10);
let html = tmpl
.render(context! {
title => "Hello minijinja",
age => 8u8,
score => 99.22,
vip => true,
note => Option::<String>::None,
tags => vec!["rust", "axum", "jinja"],
hash_map => hash_map,
status => Status::Pending,
status1 => Status::Success,
user => User {
name: "Tom".to_string(),
age: 33,
hooby: Some(vec!["sing".to_string(), "hop".to_string(), "rap".to_string(), "basketball".to_string()])
}
})
.unwrap();
Html(html)
}
#[tokio::main]
async fn main() {
let app = Router::new().route("/a", get(a));
let listener = TcpListener::bind("0.0.0.0:9090")
.await
.expect("bind failed");
println!("Server running at http://0.0.0.0:9090");
axum::serve(listener, app).await.unwrap();
}
模版复用
// 前提是这些模板都已经在 Environment 里注册好了
env.add_template("page.html", ...)?;
env.add_template("footer.html", ...)?;
env.add_template("components.html", ...)?;
- include
- 适合把公共片段插进页面,比如页头、页脚、侧边栏。
- macro
- 定义“模板函数/组件”(相当于模板里的可复用函数)
- import
- 来把别的模板里的 macro 导进来再调用
base.html
<h2>base html</h2>
macro.html
{% macro user_card(name, age) %}
<div>
<p>macro.html</p>
<p>{{ name }}</p>
<p>{{ age }}</p>
</div>
{% endmacro %}
test.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- 使用 include 导入模版-->
<p>{{ title }}</p>
<h1>引入</h1>
{% include "base" %}
<hr>
<!-- 使用import导入macro定义的内容 -->
<h1>使用方法</h1>
{% import "macro.html" as c %}
{{ c.user_card(user.name, user.age) }}
</body>
</html>
main.rs
use axum::{Router, response::Html, routing::get};
use tokio::net::TcpListener;
use tower_http::services;
use std::collections::HashMap;
use minijinja::{Environment, context};
use serde::Serialize;
// 需要实现serde::Serialize,context!才能转换
#[derive(Debug, Serialize)]
struct User {
name: String,
age: u8,
}
async fn a() -> Html<String> {
// 获取html内容
// 获取所有模板
let base_html_str = tokio::fs::read_to_string("static/base.html").await.expect("sss");
let macro_html_str = tokio::fs::read_to_string("static/macro.html").await.expect("sss");
let html_str = tokio::fs::read_to_string("static/test.html").await.expect("sss");
// 创建Environment
let mut env = Environment::new();
// 注册模版信息
// 注册所有模板
env.add_template("base", &base_html_str).unwrap();
env.add_template("macro.html", ¯o_html_str).unwrap();
env.add_template("index.html", &html_str).unwrap();
// 获取模版信息
let tmpl = env.get_template("index.html").unwrap();
let html = tmpl
.render(context! {
title => "Hello minijinja",
user => User {
name: "Tom".to_string(),
age: 33
}
})
.unwrap();
Html(html)
}
#[tokio::main]
async fn main() {
let app = Router::new().route("/", get(a));
let listener = TcpListener::bind("0.0.0.0:9090")
.await
.expect("bind failed");
println!("Server running at http://0.0.0.0:9090");
axum::serve(listener, app).await.unwrap();
}
extends/block
模板继承,
基础页面骨架 + 子页面填内容,
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}默认标题{% endblock %}</title>
</head>
<body>
<h1>{% block htitle %}默认h1{% endblock %}</h1>
<hr>
<div>
{% block content %}{% endblock %}
</div>
</body>
</html>
{% extends "base" %}
{% block title %}
{{ my_title }}
{% endblock %}
{% block htitle %}
{{ name }}
{% endblock %}
{% block content %}
<ul>
{% for i in users %}
<li>{{ i }}</li>
{% endfor %}
</ul>
{% endblock %}
use axum::{Router, response::Html, routing::get};
use tokio::net::TcpListener;
use minijinja::{Environment, context};
async fn a() -> Html<String> {
// 获取html内容
let base_html_str = tokio::fs::read_to_string("static/base.html").await.expect("sss");
let html_str = tokio::fs::read_to_string("static/test.html").await.expect("sss");
// 创建Environment
let mut env = Environment::new();
// 注册模版信息
env.add_template("base", &base_html_str).unwrap();
env.add_template("index.html", &html_str).unwrap();
// 获取模版信息
let tmpl = env.get_template("index.html").unwrap();
let html = tmpl
.render(context! {
my_title => "Hello minijinja",
name => "test html",
users => vec!["sing", "hop", "rap", "basketball"]
})
.unwrap();
Html(html)
}
#[tokio::main]
async fn main() {
let app = Router::new().route("/", get(a));
let listener = TcpListener::bind("0.0.0.0:9090")
.await
.expect("bind failed");
println!("Server running at http://0.0.0.0:9090");
axum::serve(listener, app).await.unwrap();
}
filter
就是"对输出值做加工"
字符串处理
- upper
转大写。{{ name|upper }} - lower
转小写。{{ name|lower }} - title
标题化/单词首字母大写。{{ name|title }} - trim
去掉首尾空白。{{ name|trim }} - replace
替换字符串。{{ name|replace("a", "b") }} - split
按分隔符拆分, 返回一个列表。{{ text|split(",") }}
{% for x in text|split(",") %}
<p>{{ x }}</p>
{% endfor %} - join
把列表拼成字符串。{{ tags|join(", ") }} - escape
做 HTML 转义,防止标签被当 HTML 执行
例如Hello会变成可显示的标签文本,而不是加粗<b>Hello</b> - safe
告诉模板“这段内容不再转义,按原始 HTML 输出”
常用于你明确知道内容可信的 HTML 片段。
默认值 / 判空
- default
变量不存在、为空时给默认值。{{ nickname|default("游客") }}
集合处理
-
length
取长度。{{ tags|length }} -
first
取第一个元素。{{ tags|first }} -
last
取最后一个元素。{{ tags|last }} -
sort
排序。{% for x in tags|sort %} -
reverse
反转顺序。{% for x in tags|reverse %} -
unique
去重。{% for x in tags|unique %} -
items
把 HashMap 变成 (key, value) 可迭代对。{% for k, v in my_map|items %}
数值处理
-
int
把值转成整数:{{ score_str|int }} -
float
把值转成浮点数:{{ price_str|float }} -
round
四舍五入(针对数字、浮点数){{ num|round }}
-abs
取绝对值{{ neg|abs }} -
sum
对列表求和:{{ nums|sum }} -
min
取最小值:{{ nums|min }} -
max
取最大值:{{ nums|max }}
结构化输出
- tojson
把数据转成 JSON 字符串:{{ user|tojson }} - pprint
调试时把数据更易读地打印出来:{{ user|pprint }}

浙公网安备 33010602011771号