<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Rust模板引擎askama快速入門

          共 9050字,需瀏覽 19分鐘

           ·

          2024-04-11 13:05

          模板引擎很多時(shí)候還是很有用的,無論是后端渲染網(wǎng)頁還是生成一些文本,其中以Jinja比較出名,而本文的Rustaskama正是JinjaRust版實(shí)現(xiàn),如果你對Jinja的語法比較熟悉的話,使用askama應(yīng)該不會太難上手。

          本文Cargo.toml所有代碼的依賴

                
                [dependencies]
          askama = "0.12.1"
          axum = "0.7.4"
          tokio = { version = "1.36.0", features = ["full"] }

          快速入門

          一般來說,模板都是在templates目錄下的一個(gè)文件,這里為了簡單起見這里就不使用文件的形式了。

                
                use askama::Template;


          #[derive(Template)]
          #[template(source = "Hello {{name}}", ext="txt")]
          struct QuickStart {
              name: String
          }

          fn main() {
              let quickstart = QuickStart{name: String::from("youerning.top")};
              println!("{}", quickstart.render().expect("渲染模板失敗:"));
          }

          askama支持兩種模板形式,一種就是上述這種直接通過souce屬性設(shè)置,另一種就是將模板單獨(dú)放在templates目錄下的文件里面,比如下面這樣的目錄結(jié)構(gòu)。

                
                .
          ├── Cargo.lock
          ├── Cargo.toml
          ├── src
          │   └── main.rs
          └── templates
              └── quickstart.txt

          所以上面的例子還可以這樣寫。

                
                use askama::Template;

          #[derive(Template)]
          #[template(path="quickstart.txt")]
          struct QuickStart{
            name: String,
          }

          fn main() {
            let quick_start = QuickStart{name: String::from("youerning.top")};
            println!("{}", quick_start.render().unwrap());
          }

          quickstart.txt的文件內(nèi)容如下

                
                hello {{name}}

          渲染結(jié)果都是一樣的: hello youerning.top

          值得注意的是, 其實(shí)我們可以將name字段設(shè)置成&'a str的格式, 比如下面這樣。

                
                struct QuickStart<'a> { 
              name: &'a str
          }

          這樣可以避免沒必要的內(nèi)存拷貝以提升性能,但是吧,很多人可能看到生命周期就頭疼,所以這里選擇了效率不那么高的方式,也就是直接使用String類型。

          模板屬性

          askama提供了一些可選的屬性用于配置模板的一些行為,屬性名如下

          • path,比如template(path = "foo.html")), 通過該屬性指定使用的模板文件
          • source, 比如template(source = "{{ foo }}"), 直接使用字面值定義的模板內(nèi)容,該屬性需要配合ext屬性一起使用。
          • ext,比如template(source = "{{ foo }}", ext="txt"), 指定模板的擴(kuò)展名, 不同的擴(kuò)展名代表不一樣的內(nèi)容,比如html就需要額外的轉(zhuǎn)義以避免XSS攻擊, 該屬性不能和path屬性一起使用。
          • print,比如template(print = "code")),用于debug時(shí)使用,在運(yùn)行的時(shí)候會打印宏生成的代碼code, 或者語法樹ast
          • escape,比如template(escape = "none")), 可以通過配置none來禁用轉(zhuǎn)義。
          • syntax,比如template(syntax = "foo")),  指定要使用的語法配置, 如果你想自定義一種語法的話。

          配置

          在上一節(jié)有一個(gè)可能不太容易理解的配置,那就是syntax,它的選項(xiàng)主要來源于askama提供的語法配置選項(xiàng),我們可以在根目錄創(chuàng)建一個(gè)askama.toml文件, askama在編譯的時(shí)候會讀取這個(gè)文件,下面是一個(gè)官方示例:

                
                [general]
          default_syntax = "foo"

          [[syntax]]
          name = "foo"
          block_start = "%{"
          comment_start = "#{"
          expr_end = "^^"

          [[syntax]]
          name = "bar"
          block_start = "%%"
          block_end = "%%"
          comment_start = "%#"
          expr_start = "%{"

          為啥要配置一套額外的奇怪的語法?

          比如上面的expr_start=%{, 而默認(rèn)的是{{, 這里的應(yīng)用場景其實(shí)主要是用模板渲染模板的時(shí)候比較有用(嗯, 奇怪的需求),比如使用rust渲染Python或者golang的模板, 后兩者也是使用的一樣的語法即{{

          如果你使用過helm可能會理解這個(gè)需求,假設(shè)需求是根據(jù)需求創(chuàng)建對應(yīng)的helm模板(chart), helm里面其實(shí)會用到Golang的模板語法, 如果你想做一個(gè)渲染這種模板的需求,那么你可能就需要定義一套區(qū)別于Golang渲染語法的askama語法了。

          除此之外我們還可以配置模板文件讀取的位置,如果我們不喜歡templates這個(gè)目錄名的話。

                [general]
          # 默認(rèn)情況是templates
          dirs = ["tpls"]

          語法

          就像開頭所說的, askama算得上是JinjaRust版實(shí)現(xiàn),所以語法幾乎一致, 如果你會Jinja的語法幾乎就懂askama的語法。

          變量

          這個(gè)比較簡單,name是模板結(jié)構(gòu)體struct中定義的字段,askama對于要渲染的變量類型的要求是其實(shí)現(xiàn)了Display這個(gè)trait

                {{ name }}


          賦值


          在渲染的上下文中創(chuàng)建一個(gè)新的變量。


                  
                  {% let len = name.len() %}
          name length is {{ len }}

          過濾器


          對變量或者字面值做一個(gè)額外的處理,更多的內(nèi)置的過濾器可以參考: https://djc.github.io/askama/filters.html


                  
                  {{ "hello"|capitalize }} {{ name|capitalize }}

          上面這部分的輸出如下


                  
                  Hello Youerning.top

          要注意過濾|的兩端不要有空格。


          空格控制


          渲染的時(shí)候可以選擇是否將模板中的空格消除掉,比如


                  
                  >>  {{ name }}  <<

          的渲染結(jié)果是>> youerning.top <<


          但是我們可以選擇消除兩邊的空格,比如


                  
                  >>  {{- name -}}  <<

          的渲染結(jié)果是>>youerning.top<<,可以看到, 模板語法把到非空字符之間的空格消除了,這種消除對于那些對空格敏感的格式很有用,比如yaml,又或者渲染的結(jié)果需要美化之類的。


          上面的例子是為了簡單起見,一般來說消除的是控制結(jié)構(gòu)比如iffor之類的語句的空格,比如下面這樣。


                  
                  {% if foo %}
          {{- bar -}}
          {% else if -%}
          nothing
          {%- endif %}

          askama一共支持三種空格控制的語法,第二個(gè)第三個(gè)沒用過,這里直接復(fù)制的官方說明。



          1. Suppress (-)
          2. Minimize (~)
          3. Preserve (+)

          函數(shù)


          askama支持三種形式的函數(shù)調(diào)用



          • 模板的字段
          • 靜態(tài)函數(shù)
          • Struct/Trait 的函數(shù)實(shí)現(xiàn), 也就是impl xxx這樣定義的函數(shù)

          官方示例如下


                
                #[derive(Template)]
          #[template(source = "{{ foo(123) }}", ext = "txt")]
          struct MyTemplate {
            foo: fn(u32) -> String,
          }

          其他的函數(shù)定義形式這里就不贅述了。


          模板繼承


          如果是做web開發(fā),那么對繼承應(yīng)該不陌生,頁面之間總是會存在可以復(fù)用的情況,所以我們可以將相同的部分抽離出來作為父模板, 其他模板復(fù)用父模板的相同部分,這和面向?qū)ο蟮睦^承差不多。


          首先需要一個(gè)base.html


                
                <!DOCTYPE html>
          <html lang="en">
          <head>
          <title>{% block title %}{{ title }} - My Site{% endblock %}</title>
          {% block head %}{% endblock %}
          </head>
          <body>
          <div id="content">
          {% block content %}<p>Placeholder content</p>{% endblock %}
          </div>
          </body>
          </html>

          其中{% block content %}{% endblock %}包裹的部分就是我們可以替換的部分。


          子模板像下面這樣繼承即可。


                
                {% extends "base.html" %}

          {% block title %}Index{% endblock %}

          {% block head %}
          <style>
          </style>
          {% endblock %}

          {% block content %}
          <h1>Index</h1>
          <p>Hello, world!</p>
          {% call super() %}
          {% endblock %}

          除了繼承,我們可以使用include指令來渲染一些通用的部分,比如:


                
                {% for i in iter %}
          {% include "item.html" %}
          {% endfor %}

          item.html的內(nèi)容如下:


                
                * Item: {{ i }}

          控制結(jié)構(gòu)


          之所以要使用模板引擎而不是format!宏的一個(gè)很大的原因在于模板引擎支持控制結(jié)構(gòu),也就是循環(huán)和判斷。


          for循環(huán)


                
                {% for char in name.chars() %}
            -{{loop.index}}: {{ char|upper }}
          {% endfor %}

          if判斷


                
                {% if name == 0 %}
            youerning.top
          {% else if name.len() == 1 %}
            {{ name | upper }}
          {% else %}
            {{ name }}
          {% endif %}

          表達(dá)式


                
                {{ 3 * 4 / 2 }}
          {{ 26 / 2 % 7 }}


          要注意表達(dá)式不要出現(xiàn)遞歸



          注釋


                
                {# A Comment #}

          結(jié)合web框架axum使用


          最后來一個(gè)與axum一起使用的例子作為結(jié)尾吧。


          rust代碼如下:


                
                
          use axum::{
              http::StatusCode, response::{Html, IntoResponse, Response}, routing::get, Router
          };
          use askama::Template;

          #[derive(Template, Default)]
          #[template(path = "index.html")]
          struct Index{
              title: String,
              content: String,
          }

          struct TemplateResponse<T>(T);

          impl<T> IntoResponse for TemplateResponse<T>
          where
              T: Template,
          {
              fn into_response(self) -> Response {
                  match self.0.render() {
                      Ok(html) => Html(html).into_response(),
                      Err(err) => (
                          StatusCode::INTERNAL_SERVER_ERROR,
                          format!("Failed to render template. Error: {err}"),
                      )
                          .into_response(),
                  }
              }
          }


          pub async fn index() -> impl IntoResponse {
              TemplateResponse(Index{
                  title: "askama快速入門".into(),
                  content: "做人最重要的就是開心啦.".into(),
              })
          }

          #[tokio::main]
          async fn main() {
              let app = Router::new().route("/", get(index));

              let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
              println!("服務(wù)監(jiān)聽在 127.0.0.1:3000");
              axum::serve(listener, app).await.unwrap();
          }

          index.html的內(nèi)容如下:


                
                <!DOCTYPE html>
          <html lang="en">
            <head>
              <title>{{ title }} - youerning.top</title>
            </head>
            <body>
              <div id="content" style="text-align: center;">
                {{content}}
              </div>
            </body>
          </html>

          總結(jié)


          每門編程語言幾乎都有模板引擎的,其實(shí)差別不大,但是根據(jù)語言特性的不同可能會稍稍改動,比如askama還能在模板中使用match語句, 以及變量賦值的時(shí)候需要在前面加個(gè)let, 這些都是編程語言特性帶來的。


          參考鏈接



          • https://docs.rs/askama/latest/askama/#the-template-attribute
          • https://djc.github.io/askama/askama.html

          瀏覽 58
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  伊人久久免费视频 | 中国一级久久毛 | 精品久久久久久18禁免费网站 | 国产精品欧美一区二区三区苍井空 | 五月涩欧美 |