Cookbook / Files and pages

HTML templates

Server-rendered pages with auto-escaping templates, layouts and form handling.

Templates use minijinja, which follows the Jinja syntax: inheritance, loops, filters and macros. Output is HTML-escaped by default, so user input cannot inject markup.

src/main.rs
use axeon::templates::Templates;
use axeon::{json, Server, ServerError};
use serde::Deserialize;

#[derive(Deserialize)]
struct Comment {
    author: String,
    text: String,
}

fn main() {
    let templates = Templates::new()
        .add("layout.html", "<!doctype html><title>{% block title %}{% endblock %}</title>{% block body %}{% endblock %}")
        .unwrap()
        .add(
            "hello.html",
            "{% extends 'layout.html' %}\
             {% block title %}Hello {{ name }}{% endblock %}\
             {% block body %}<h1>Hello, {{ name }}!</h1>\
             <ul>{% for c in comments %}<li><b>{{ c.author }}</b>: {{ c.text }}</li>{% endfor %}</ul>\
             {% endblock %}",
        )
        .unwrap();
    // In a real app, load a directory instead: Templates::from_dir("views")

    let mut app = Server::new();
    app.templates(templates);

    app.get("/hello/:name", |req| async move {
        req.render(
            "hello.html",
            json!({
                "name": req.params()["name"],
                "comments": [{ "author": "Ada", "text": "<script>alert(1)</script>" }],
            }),
        )
    });

    // An HTML form posts urlencoded data.
    app.post("/comments", |req| async move {
        let comment: Comment = req.form()?;
        Ok::<_, ServerError>((201, format!("thanks {}: {}", comment.author, comment.text)))
    });

    app.listen("127.0.0.1:3000").expect("server failed to start");
}
Terminal
curl http://127.0.0.1:3000/hello/Grace       # the <script> comes out escaped
curl -X POST http://127.0.0.1:3000/comments -d 'author=Ada&text=hi'

Notes#

  • Templates::from_dir("views") loads every file under the directory, so templates are named by their relative path.
  • Add Csrf middleware (see Cookies, sessions and CSRF) to any app that posts HTML forms.