Working with web-frameworks
Askama’s Template::render() returns Result<String, askama::Error>.
To make this result work in your preferred web-framework, you’ll need to handle both cases:
converting the String to a web-response with the correct Content-Type,
and the Error case to a proper error message.
While in many cases it will be enough to simply convert the Error to
Box<dyn std::error::Error + Send + Sync>usingerr.into_box()orstd::io::Errorusingerr.into_io_error()
it is recommended to use a custom error type. This way you can display the error message in your app’s layout, and you are better prepared for the likely case that your app grows in the future. Maybe you’ll need to access a database and handle errors? Maybe you’ll add multiple languages and you want to localize error messages?
The crates thiserror and displaydoc can be useful to implement this error type.
Simplified alternative
Alternatively, you can use #[derive(askama_web::WebTemplate)]
to automatically implement e.g. actix-web’s Responder, axum’s IntoResponse or warp’s Reply.
The library implements traits for all web-frameworks mentioned down below (and then some),
but it does not stylize error messages.
If you don’t need custom / stylized error messages,
e.g. because you know that your templates won’t have rendering errors, then using
askama_web might work for you, too.
Actix-Web
Install using the following command to enable the actix-web-4 feature for this framework
(check the available features for other versions):
cargo add askama_web --features "actix-web-4"
See the actix-web example web-app.
To convert the String to an HTML response, you can use
Html::new(_).
#![allow(unused)]
fn main() {
use actix_web::web::Html;
use actix_web::{Responder, handler};
#[handler]
fn handler() -> Result<impl Responder, AppError> {
…
Ok(Html::new(template.render()?))
}
}
To implement your own error type, you can use this boilerplate code:
#![allow(unused)]
fn main() {
use actix_web::{HttpResponse, Responder};
use actix_web::error::ResponseError;
use actix_web::http::StatusCode;
use actix_web::web::Html;
use askama::Template;
#[derive(Debug, displaydoc::Display, thiserror::Error)]
enum AppError {
/// could not render template
Render(#[from] askama::Error),
}
impl ResponseError for AppError {
fn status_code(&self) -> StatusCode {
match &self {
AppError::Render(_) => StatusCode::INTERNAL_SERVER_ERROR,
}
}
}
impl Responder for AppError {
type Body = String;
fn respond_to(self, req: &HttpRequest) -> HttpResponse<Self::Body> {
#[derive(Debug, Template)]
#[template(path = "error.html")]
struct Tmpl { … }
let tmpl = Tmpl { … };
if let Ok(body) = tmpl.render() {
(Html::new(body), self.status_code()).respond_to(req)
} else {
(String::new(), self.status_code()).respond_to(req)
}
}
}
}
Axum
Install using the following command to enable the axum-0.8 feature for this framework
(check the available features for other versions):
cargo add askama_web --features "axum-0.8"
To convert the String to an HTML response, you can use
Html(_).
#![allow(unused)]
fn main() {
use axum::response::{Html, IntoResponse};
async fn handler() -> Result<impl IntoResponse, AppError> {
…
Ok(Html(template.render()?))
}
}
To implement your own error type, you can use this boilerplate code:
#![allow(unused)]
fn main() {
use axum::response::IntoResponse;
use askama::Template;
#[derive(Debug, displaydoc::Display, thiserror::Error)]
enum AppError {
/// could not render template
Render(#[from] askama::Error),
}
impl IntoResponse for AppError {
fn into_response(self) -> Response {
#[derive(Debug, Template)]
#[template(path = "error.html")]
struct Tmpl { … }
let status = match &self {
AppError::Render(_) => StatusCode::INTERNAL_SERVER_ERROR,
};
let tmpl = Tmpl { … };
if let Ok(body) = tmpl.render() {
(status, Html(body)).into_response()
} else {
(status, "Something went wrong").into_response()
}
}
}
}
Poem
Install using the following command to enable the poem-3 feature for this framework
(check the available features for other versions):
cargo add askama_web --features "poem-3"
To convert the String to an HTML response, you can use
Html(_).
#![allow(unused)]
fn main() {
use poem::web::Html;
use poem::{IntoResponse, handler};
#[handler]
async fn handler() -> Result<impl IntoResponse, AppError> {
…
Ok(Html(template.render()?))
}
}
To implement your own error type, you can use this boilerplate code:
#![allow(unused)]
fn main() {
use poem::error::ResponseError;
use poem::http::StatusCode;
use poem::web::Html;
use poem::{IntoResponse, Response};
use askama::Template;
#[derive(Debug, displaydoc::Display, thiserror::Error)]
enum AppError {
/// could not render template
Render(#[from] askama::Error),
}
impl ResponseError for AppError {
fn status(&self) -> StatusCode {
match &self {
AppError::Render(_) => StatusCode::INTERNAL_SERVER_ERROR,
}
}
}
impl IntoResponse for AppError {
fn into_response(self) -> Response {
#[derive(Debug, Template)]
#[template(path = "error.html")]
struct Tmpl { … }
let tmpl = Tmpl { … };
if let Ok(body) = tmpl.render() {
(self.status(), Html(body)).into_response()
} else {
(self.status(), "Something went wrong").into_response()
}
}
}
}
Rocket
Install using the following command to enable the rocket-0.5 feature for this framework
(check the available features for other versions):
cargo add askama_web --features "rocket-0.5"
See the rocket example web-app.
To convert the String to an HTML response, you can use
RawHtml(_).
#![allow(unused)]
fn main() {
use rocket::get;
use rocket::response::content::RawHtml;
use rocket::response::Responder;
#[get(…)]
fn handler<'r>() -> Result<impl Responder<'r, 'static>, AppError> {
…
Ok(RawHtml(template.render()?))
}
}
To implement your own error type, you can use this boilerplate code:
#![allow(unused)]
fn main() {
use askama::Template;
use rocket::http::Status;
use rocket::response::content::RawHtml;
use rocket::response::Responder;
use rocket::{Request, Response};
#[derive(Debug, displaydoc::Display, thiserror::Error)]
enum AppError {
/// could not render template
Render(#[from] askama::Error),
}
impl<'r> Responder<'r, 'static> for AppError {
fn respond_to(
self,
request: &'r Request<'_>,
) -> Result<Response<'static>, Status> {
#[derive(Debug, Template)]
#[template(path = "error.html")]
struct Tmpl { … }
let status = match &self {
AppError::Render(_) => Status::InternalServerError,
};
let template = Tmpl { … };
if let Ok(body) = template.render() {
(status, RawHtml(body)).respond_to(request)
} else {
(status, "Something went wrong").respond_to(request)
}
}
}
}
Warp
Install using the following command to enable the wrap-0.4 feature for this framework
(check the available features for other versions):
cargo add askama_web --features "wrap-0.4"
To convert the String to an HTML response, you can use
html(_).
#![allow(unused)]
fn main() {
use warp::reply::{Reply, html};
fn handler() -> Result<impl Reply, AppError> {
…
Ok(html(template.render()?))
}
}
To implement your own error type, you can use this boilerplate code:
#![allow(unused)]
fn main() {
use http::StatusCode;
use warp::reply::{Reply, Response, html};
#[derive(Debug, displaydoc::Display, thiserror::Error)]
enum AppError {
/// could not render template
Render(#[from] askama::Error),
}
impl Reply for AppError {
fn into_response(self) -> Response {
#[derive(Debug, Template)]
#[template(path = "error.html")]
struct Tmpl { … }
let status = match &self {
AppError::Render(_) => StatusCode::INTERNAL_SERVER_ERROR,
};
let template = Tmpl { … };
if let Ok(body) = template.render() {
with_status(html(body), status).into_response()
} else {
status.into_response()
}
}
}
}