Rust web 框架 axum (三): 使用 askama html 模板

文章目录

    askama 是什么

    Askama implements a template rendering engine based on Jinja. It generates Rust code from your templates at compile time based on a user-defined struct to hold the template’s context.

    askama 即一套模板系统,可以集成到 rust 项目中,并不局限于 axum 中,也不局限于 HTML,任何的模板使用场景都是可以的。

    类似 golang 内置的模板系统,及 php laravel 中的 blade,python django 的模板。语法都非常类似。

    项目地址:

    https://github.com/djc/askama

    为何要使用 askama 这里的模板系统

    • 可以将 HTML 代码写在独立的文件中,这样就不需要像上一篇 axum 介绍中 axum 返回 HTML form 表单,并处理 post 请求,将 HTML 代码混在 rust 代码中。这样不方便编辑,也无法高效利用编辑器或 IDE 的增强功能。
    • 可以基于 base 模板做继承,方便保证一个网站所有页面的风格统一,及 HTML 代码的模块化,及复用。
    • 在代码中实现一些 if,及 for 逻辑。

    askama V.S. minijinja

    • 2.6K https://github.com/djc/askama
    • 1.1K https://github.com/mitsuhiko/minijinja

    添加 askama 依赖

    编辑 Cargo.toml 配置文件,增加 askama 及 askama_axum 依赖

    [dependencies]
    askama = { version = "0.12", features = ["with-axum", "mime", "mime_guess"] }
    askama_axum = "0.3.0"
    axum = "0.6"
    

    新增模板目录

    mkdir templates
    

    新增 html 模板

    例如,base.html

    内容可以随便写,即一个简单的 HTML 文件。

    <h1>Hello </h1>
    

    axum 中使用

    use askama::Template;
    
    #[derive(Template)]
    #[template(path = "base.html")]
    struct HelloTemplate<'a> {
        name: &'a str,
    }
    
    async fn hello_template() -> HelloTemplate<'static> {
        HelloTemplate { name: "world" }
    }
    

    然后 route 中使用这个 hello_template 即可

    let app = Router::new()
        .route("/some_path", get(hello_template))
        ;
    

    到此,axum 与 askama 的集成完成。以下是测试过程中遇到的问题。

    单引号 a 与生命周期

    下面 rust 代码中,单引号 a 的作用是什么:

    struct HelloTemplate<'a> {
        name: &'a str,
    }
    

    在 Rust 代码中,单引号 'a 用于定义一个生命周期参数。在这个特定的代码片段中,'a 是用于泛型结构体 HelloTemplate 的生命周期参数。

    生命周期参数用于描述引用的有效期,以确保在引用的数据不再有效之前,引用本身仍然有效。在这种情况下,'a 表示 name 字段的引用的生命周期。

    HelloTemplate 结构体中的 name 字段的类型是 &'a str,意味着它是一个指向字符串的引用,该引用的生命周期与结构体的生命周期参数 'a 相关联。这意味着 name 字段的引用不能超过 'a 所表示的生命周期。

    通过使用生命周期参数,Rust 的借用检查器可以在编译时验证引用的有效性,防止悬垂引用或无效引用的出现,从而提供内存安全性和避免潜在的错误。

    askama 模板继承

    https://djc.github.io/askama/template_syntax.html#child-template

    {% extends "base.html" %}
    
    {% block title %}Index{% endblock %}
    
    {% block head %}
      <style>
      </style>
    {% endblock %}
    
    {% block content %}
      <h1>Index</h1>
      <p>Hello, world!</p>
      {% call super() %}
    {% endblock %}
    

    askama 官方依赖配置示例

    https://djc.github.io/askama/integrations.html#axum-integration

    Enabling the with-axum feature appends an implementation of Axum’s IntoResponse trait for each template type. This makes it easy to trivially return a value of that type in a Axum handler. See the example from the Askama test suite for more on how to integrate.

    如官方 github 的 axum 整合示例中的 Cargo.toml 依赖配置

    https://github.com/djc/askama/blob/main/askama_axum/Cargo.toml

    [dependencies]
    askama = { version = "0.12", path = "../askama", default-features = false, features = ["with-axum", "mime", "mime_guess"] }
    

    需要 enable with-axum feature。

    而,我之前配置的是:

    [dependencies]
    askama = "0.12.1"
    

    于是我参考官方配置修改成了

    askama = { version = "0.12", default-features = false, features = ["with-axum", "mime", "mime_guess"] }
    

    askama 官方代码示例

    https://github.com/djc/askama/blob/main/askama_axum/tests/basic.rs

    could not find askama_axum in the list of imported crates

    error[E0433]: failed to resolve: could not find `askama_axum` in the list of imported crates
       --> src/main.rs:101:10
        |
    101 | #[derive(Template)]
        |          ^^^^^^^^ could not find `askama_axum` in the list of imported crates
        |
        = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info)
    

    在 Cargo.toml 中添加了 askama_axum 之后,就可以了。

    [dependencies]
    askama = { version = "0.12", features = ["with-axum", "mime", "mime_guess"] }
    askama_axum = "0.3.0"
    axum = "0.6"
    

    unknown field

    error[E0609]: no field `title` on type `&HelloTemplate<'a>`
       --> src/main.rs:101:10
        |
    101 | #[derive(Template)]
        |          ^^^^^^^^ unknown field
        |
        = note: available fields are: `name`
        = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info)
    

    将模板文件中的 title 改为 name,就可以编译通过了。

    rust 三方依赖包,为何会有 features 的概念

    在 Rust 的第三方依赖包中,features 的概念用于启用或禁用库的特定功能或选项。
    这允许你根据你的项目需求,在构建依赖包时进行选择性地启用或禁用功能。

    许多依赖包提供了一些可选的功能,但不是所有用户都需要或希望使用这些功能。
    为了保持库的灵活性和轻量性,开发人员将这些功能作为可选项提供,而不是默认包含在库中。

    查看合集

    📖 Rust web 框架 axum 教程:从入门到遥遥领先

    参考

    • https://www.reddit.com/r/rust/comments/11gizhc/axum_sqlite_minijinja_htmx_winning_website_combo/
    • bootstrap 模板: https://getbootstrap.com/docs/5.3/examples/jumbotron/
    • askama 的官方整合教程: https://github.com/djc/askama/blob/main/askama_axum/tests/basic.rs
    • 基于 axum/askama/htmx 的教程: https://www.joeymckenzie.tech/blog/templates-with-rust-axum-htmx-askama/

    关于作者 🌱

    我是来自山东烟台的一名开发者,有感兴趣的话题,或者软件开发需求,欢迎加微信 zhongwei 聊聊,或者关注我的个人公众号“大象工具”, 查看更多联系方式