diff --git a/template_handlebars/Cargo.toml b/template_handlebars/Cargo.toml index dcae8704..ddb8297b 100644 --- a/template_handlebars/Cargo.toml +++ b/template_handlebars/Cargo.toml @@ -5,7 +5,8 @@ authors = ["Alexandru Tiniuc "] edition = "2018" [dependencies] -actix-web = "2.0.0" +actix-http = "1.0.1" actix-rt = "1.0.0" +actix-web = "2.0.0" handlebars = { version = "3.0.0", features = ["dir_source"] } serde_json = "1.0" diff --git a/template_handlebars/src/README.md b/template_handlebars/src/README.md index 7fc81da3..9ee3e6ab 100644 --- a/template_handlebars/src/README.md +++ b/template_handlebars/src/README.md @@ -5,3 +5,4 @@ This is an example of how to use Actix Web with the [Handlebars templating langu - http://localhost:8080 - http://localhost:8080/Emma/documents - http://localhost:8080/Bob/passwords +- http://localhost:8080/some-non-existing-page - 404 error rendered using template diff --git a/template_handlebars/src/main.rs b/template_handlebars/src/main.rs index a0da7ffc..60f15240 100644 --- a/template_handlebars/src/main.rs +++ b/template_handlebars/src/main.rs @@ -4,8 +4,11 @@ extern crate actix_web; #[macro_use] extern crate serde_json; -use actix_web::web; -use actix_web::{App, HttpResponse, HttpServer}; +use actix_http::{body::Body, Response}; +use actix_web::dev::ServiceResponse; +use actix_web::http::StatusCode; +use actix_web::middleware::errhandlers::{ErrorHandlerResponse, ErrorHandlers}; +use actix_web::{web, App, HttpResponse, HttpServer, Result}; use handlebars::Handlebars; @@ -49,6 +52,7 @@ async fn main() -> io::Result<()> { HttpServer::new(move || { App::new() + .wrap(error_handlers()) .app_data(handlebars_ref.clone()) .service(index) .service(user) @@ -57,3 +61,50 @@ async fn main() -> io::Result<()> { .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( + res.into_response(response.into_body()), + )) +} + +// Generic error handler. +fn get_error_response(res: &ServiceResponse, error: &str) -> Response { + 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 = |e: &str| { + Response::build(res.status()) + .content_type("text/plain") + .body(e.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) => Response::build(res.status()) + .content_type("text/html") + .body(body), + Err(_) => fallback(error), + } + } + None => fallback(error), + } +} diff --git a/template_handlebars/static/templates/error.html b/template_handlebars/static/templates/error.html new file mode 100644 index 00000000..ab0b1922 --- /dev/null +++ b/template_handlebars/static/templates/error.html @@ -0,0 +1,10 @@ + + + + + {{error}} + + +

{{status_code}} {{error}}

+ + diff --git a/template_tera/Cargo.toml b/template_tera/Cargo.toml index 6e45269e..591a577d 100644 --- a/template_tera/Cargo.toml +++ b/template_tera/Cargo.toml @@ -8,5 +8,6 @@ edition = "2018" [dependencies] env_logger = "0.7" tera = "1.0" +actix-http = "1.0.1" actix-web = "2.0.0" actix-rt = "1.0.0" diff --git a/template_tera/README.md b/template_tera/README.md index 9cc14f46..12aa7a6e 100644 --- a/template_tera/README.md +++ b/template_tera/README.md @@ -15,3 +15,4 @@ cargo run (or ``cargo watch -x run``) ### web client - [http://localhost:8080](http://localhost:8080) +- [http://localhost:8080/non-existing-page](http://localhost:8080/non-existing-page) - 404 page rendered using template diff --git a/template_tera/src/main.rs b/template_tera/src/main.rs index fd265ab1..85fdd13c 100644 --- a/template_tera/src/main.rs +++ b/template_tera/src/main.rs @@ -1,6 +1,10 @@ use std::collections::HashMap; -use actix_web::{error, middleware, web, App, Error, HttpResponse, HttpServer}; +use actix_http::{body::Body, Response}; +use actix_web::dev::ServiceResponse; +use actix_web::http::StatusCode; +use actix_web::middleware::errhandlers::{ErrorHandlerResponse, ErrorHandlers}; +use actix_web::{error, middleware, web, App, Error, HttpResponse, HttpServer, Result}; use tera::Tera; // store tera template in application state @@ -35,8 +39,53 @@ async fn main() -> std::io::Result<()> { .data(tera) .wrap(middleware::Logger::default()) // enable logger .service(web::resource("/").route(web::get().to(index))) + .service(web::scope("").wrap(error_handlers())) }) .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( + res.into_response(response.into_body()), + )) +} + +// Generic error handler. +fn get_error_response(res: &ServiceResponse, error: &str) -> Response { + 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 = |e: &str| { + Response::build(res.status()) + .content_type("text/plain") + .body(e.to_string()) + }; + + let tera = request.app_data::>().map(|t| t.get_ref()); + match tera { + Some(tera) => { + let mut context = tera::Context::new(); + context.insert("error", error); + context.insert("status_code", res.status().as_str()); + let body = tera.render("error.html", &context); + + match body { + Ok(body) => Response::build(res.status()) + .content_type("text/html") + .body(body), + Err(_) => fallback(error), + } + } + None => fallback(error), + } +} diff --git a/template_tera/templates/error.html b/template_tera/templates/error.html new file mode 100644 index 00000000..4a3b512f --- /dev/null +++ b/template_tera/templates/error.html @@ -0,0 +1,10 @@ + + + + + {{ error }} + + +

{{ status_code }} {{ error }}

+ +