axum使用minijinja.md

构建流程

  1. 创建模板环境
    • let mut env = Environment::new();
  2. 注册模板
    • env.add_template(name: &str, source: &str)
      • name 是模板名,你自定义即可
      • source 是模板内容
    • env.add_template("index.html", "<h1>{{ name }}</h1>").unwrap();
  3. 获取模板
    • let tmpl = env.get_template(name: &str).unwrap();
      • 这个name需要和注册的name一致
    • let tmpl = env.get_template("index.html").unwrap();
  4. 渲染模板
    • 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中

  • 在异步任务中,使用tokiofs读取文件内容
// 读取文件内容(阻塞读取的)
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", &macro_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会变成可显示的标签文本,而不是加粗&lt;b&gt;Hello&lt;/b&gt
  • 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 }}
posted @ 2026-08-11 14:33  lxd670  阅读(2)  评论(0)    收藏  举报