I tried to make actix-web
and Tera
work together, from examples I found online.
This is my code:
use actix_web::{get, web, Result, App, HttpServer, HttpRequest, Responder, HttpResponse};
use serde::{Deserialize,Serialize};
use tera::{Tera, Context};
use lazy_static::lazy_static;
lazy_static! {
pub static ref TEMPLATES: Tera = {
let mut tera = match Tera::new("templates/**/*.html") {
Ok(t) => t,
Err(e) => {
println!("Parsing error(s): {}", e);
::std::process::exit(1);
}
};
tera.autoescape_on(vec![".html", ".sql"]);
tera
};
}
#[derive(Serialize)]
pub struct Response {
pub message: String,
}
#[get("/")]
async fn index(tera: web::Data<Tera>) -> impl Responder {
let mut context = Context::new();
let template = tera.render("test.page.html", &context).expect("Error");
HttpResponse::Ok().body(template)
}
async fn not_found() -> Result<HttpResponse> {
let response = Response {
message: "Resource not found".to_string(),
};
Ok(HttpResponse::NotFound().json(response))
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
std::env::set_var("RUST_LOG", "debug");
env_logger::init();
HttpServer::new(|| App::new()
.service(index)
.default_service(web::route().to(not_found)))
.bind(("127.0.0.1", 9090))?
.run()
.await
}
But when I go to the url I get this error:
[2023-08-29T11:27:53Z INFO actix_server::builder] starting 8 workers
[2023-08-29T11:27:53Z INFO actix_server::server] Actix runtime found; starting in Actix runtime
[2023-08-29T11:28:07Z DEBUG actix_web::data] Failed to extract `Data<tera::tera::Tera>` for `index` handler. For the Data extractor to work correctly, wrap the data with `Data::new()` and pass it to `App::app_data()`. Ensure that types align in both the set and retrieve calls.
Why Do I need to wrap it in Data::new()
and use App::app_data()
?
Why Do I need to wrap it in
Data::new()
and useApp::app_data()
?
Because Actix can't pass the extractor to your handler if it doesn't know about it.
Since you have used a lazy_static
to have just one instance of Tera
, you don't need to pass it to your handler as an extractor - just use the static!
#[get("/")]
async fn index() -> impl Responder {
let mut context = Context::new();
let template = TEMPLATES.render("test.page.html", &context).expect("Error");
HttpResponse::Ok().body(template)
}
But you don't need to use lazy_static
. Actix-web lets you define a shared resource and make it available to every request using an extractor. It's not clear which approach you wanted to take (since you did a bit of both!) but the other way, using shared state, is like this:
fn create_templates() - Tera {
let mut tera = Tera::new("templates/**/*.html").expect("failed to parse template");
tera.autoescape_on(vec![".html", ".sql"]);
tera
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| App::new()
.service(index)
.app_data(Data::new(create_templates()))
.default_service(web::route().to(not_found)))
.bind(("127.0.0.1", 9090))?
.run()
.await
}
#[get("/")]
async fn index(tera: web::Data<Tera>) -> impl Responder {
let mut context = Context::new();
let template = tera.render("test.page.html", &context).expect("Error");
HttpResponse::Ok().body(template)
}