use std::io; use actix_web::{ body::BoxBody, dev::ServiceResponse, get, http::{header::ContentType, StatusCode}, middleware::{ErrorHandlerResponse, ErrorHandlers}, web, App, HttpResponse, HttpServer, Responder, Result, }; use handlebars::{DirectorySourceOptions, Handlebars}; use serde_json::json; #[get("/")] async fn index(hb: web::Data>) -> impl Responder { let data = json!({ "name": "Handlebars" }); let body = hb.render("index", &data).unwrap(); web::Html::new(body) } #[get("/{user}/{data}")] async fn user(hb: web::Data>, path: web::Path<(String, String)>) -> impl Responder { let info = path.into_inner(); let data = json!({ "user": info.0, "data": info.1 }); let body = hb.render("user", &data).unwrap(); web::Html::new(body) } #[actix_web::main] async fn main() -> io::Result<()> { env_logger::init_from_env(env_logger::Env::new().default_filter_or("info")); // Handlebars uses a repository for the compiled templates. This object must be // shared between the application threads, and is therefore passed to the // Application Builder as an atomic reference-counted pointer. let mut handlebars = Handlebars::new(); handlebars .register_templates_directory( "./templates", DirectorySourceOptions { tpl_extension: ".html".to_owned(), hidden: false, temporary: false, }, ) .unwrap(); let handlebars_ref = web::Data::new(handlebars); HttpServer::new(move || { App::new() .wrap(error_handlers()) .app_data(handlebars_ref.clone()) .service(index) .service(user) }) .workers(2) .bind(("127.0.0.1", 8080))? .run() .await } // Custom error handlers, to return HTML responses when an error occurs. fn error_handlers() -> ErrorHandlers { ErrorHandlers::new().handler(StatusCode::NOT_FOUND, not_found) } // Error handler for a 404 Page not found error. fn not_found(res: ServiceResponse) -> Result> { let response = get_error_response(&res, "Page not found"); Ok(ErrorHandlerResponse::Response(ServiceResponse::new( res.into_parts().0, response.map_into_left_body(), ))) } // Generic error handler. fn get_error_response(res: &ServiceResponse, error: &str) -> HttpResponse { let request = res.request(); // Provide a fallback to a simple plain text response in case an error occurs during the // rendering of the error page. let fallback = |err: &str| { HttpResponse::build(res.status()) .content_type(ContentType::plaintext()) .body(err.to_string()) }; let hb = request .app_data::>() .map(|t| t.get_ref()); match hb { Some(hb) => { let data = json!({ "error": error, "status_code": res.status().as_str() }); let body = hb.render("error", &data); match body { Ok(body) => HttpResponse::build(res.status()) .content_type(ContentType::html()) .body(body), Err(_) => fallback(error), } } None => fallback(error), } }