1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
use actix_web::{error::ResponseError, web, HttpResponse, Responder};
use lazy_static::lazy_static;
use sailfish::TemplateOnce;
use crate::errors::PageError;
pub trait Errorable: TemplateOnce {
fn get_error_resp<E: ResponseError>(self, e: E) -> HttpResponse;
}
#[macro_export]
macro_rules! ImplErrorable {
($struct:ident) => {
impl crate::pages::errors::Errorable for $struct {
fn get_error_resp<E>(mut self, e: E) -> actix_web::HttpResponse
where
E: actix_web::error::ResponseError + std::fmt::Display,
{
self.error = Some(e.to_string());
let page = self.render_once().unwrap();
println!("status code: {}", e.status_code());
actix_web::dev::HttpResponseBuilder::new(e.status_code())
.content_type("text/html; charset=utf-8")
.body(&page)
.into()
}
}
};
}
#[derive(Clone, TemplateOnce)]
#[template(path = "errors/index.html")]
struct ErrorPage<'a> {
title: &'a str,
message: &'a str,
}
const PAGE: &str = "Error";
impl<'a> ErrorPage<'a> {
fn new(title: &'a str, message: &'a str) -> Self {
ErrorPage { title, message }
}
}
lazy_static! {
static ref INTERNAL_SERVER_ERROR_BODY: String = ErrorPage::new(
"Internal Server Error",
&format!("{}", PageError::InternalServerError),
)
.render_once()
.unwrap();
static ref UNKNOWN_ERROR_BODY: String = ErrorPage::new(
"Something went wrong",
&format!("{}", PageError::InternalServerError),
)
.render_once()
.unwrap();
}
async fn error(path: web::Path<usize>) -> impl Responder {
let resp = match path.0 {
500 => HttpResponse::InternalServerError()
.content_type("text/html; charset=utf-8")
.body(&*INTERNAL_SERVER_ERROR_BODY),
_ => HttpResponse::InternalServerError()
.content_type("text/html; charset=utf-8")
.body(&*UNKNOWN_ERROR_BODY),
};
resp
}
pub fn services(cfg: &mut web::ServiceConfig) {
use crate::define_resource;
define_resource!(cfg, "/error/{id}", Methods::Get, error);
}
pub mod routes {
pub struct Errors {
pub internal_server_error: &'static str,
pub unknown_error: &'static str,
}
impl Errors {
pub const fn new() -> Self {
Errors {
internal_server_error: "/error/500",
unknown_error: "/error/007",
}
}
}
}
#[cfg(test)]
mod tests {
use actix_web::{http::StatusCode, test, App};
use super::*;
use crate::PAGES;
#[actix_rt::test]
async fn error_pages_work() {
let mut app = test::init_service(App::new().configure(services)).await;
let resp = test::call_service(
&mut app,
test::TestRequest::get()
.uri(PAGES.errors.internal_server_error)
.to_request(),
)
.await;
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
let resp = test::call_service(
&mut app,
test::TestRequest::get()
.uri(PAGES.errors.unknown_error)
.to_request(),
)
.await;
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
}
}