server/src/web_server/app_error.rs

138 lines
4.4 KiB
Rust

use derive_more::Display;
use fred::prelude::*;
use ntex::http::{StatusCode};
use ntex::web;
use ntex::web::{HttpRequest, HttpResponse};
use thiserror::Error;
use crate::insert_header;
use crate::web_server::old_device_sensor_api::qs_parser::QSParserError;
use rust_decimal::Decimal;
use serde_json::json;
/// Главный объект ошибки [std::error::Error] для всего Web API.
///
/// В целом, все Result у Web сервера должны использовать этот Error.
#[derive(Debug, Error, Display)]
pub enum AppError {
#[display(fmt = "IDK")]
JsonError(#[from] serde_json::Error),
#[display(fmt = "IDK")]
QSError(QSParserError),
#[display(fmt = "IDK")]
ServerRedisError(#[from] RedisError),
#[display(fmt = "IDK")]
UnknownMethod(String),
#[display(fmt = "IDK")]
RequestTooLarge,
#[display(fmt = "IDK")]
ApiKeyInvalid { reason: &'static str },
#[display(fmt = "IDK")]
UnitValidationFailed {
max: Option<Decimal>,
min: Option<Decimal>,
},
#[display(fmt = "IDK")]
UnknownBody {
json_err: Option<serde_json::Error>,
query_error: Option<serde_qs::Error>,
},
}
impl web::error::WebResponseError for AppError {
fn status_code(&self) -> StatusCode {
match self {
AppError::JsonError(_) => StatusCode::BAD_REQUEST,
AppError::UnknownMethod(_) => StatusCode::BAD_REQUEST,
AppError::UnitValidationFailed { .. } => StatusCode::BAD_REQUEST,
AppError::RequestTooLarge => StatusCode::PAYLOAD_TOO_LARGE,
AppError::ServerRedisError(_) => StatusCode::INTERNAL_SERVER_ERROR,
AppError::ApiKeyInvalid { .. } => StatusCode::BAD_REQUEST,
AppError::UnknownBody { .. } => StatusCode::BAD_REQUEST,
AppError::QSError(..) => StatusCode::BAD_REQUEST,
}
}
fn error_response(&self, _: &HttpRequest) -> HttpResponse {
let error_message = match self {
AppError::JsonError(_) => "Invalid JSON",
AppError::UnknownMethod(_) => "Unknown command",
AppError::UnitValidationFailed { .. } => "Unknown command",
AppError::RequestTooLarge => "Request is too large",
AppError::ServerRedisError(_) => "Internal server error",
AppError::ApiKeyInvalid { .. } => "API Key invalid",
AppError::UnknownBody { .. } => {
"Can't figure out where and in what encoding the main data is"
}
AppError::QSError(..) => "UrlEncoded body or query params are incorrect",
};
let status_code = self.status_code();
let body = json!({
"errno": status_code.as_u16(),
"error": error_message,
});
let mut resp = HttpResponse::build(status_code).json(&body);
let headers = resp.headers_mut();
let error_as_string = format!("{:?}", &self);
match self {
AppError::JsonError(json_err) => {
insert_header!(headers, "X-Error-Line", json_err.line());
insert_header!(headers, "X-Error-Column", json_err.column());
insert_header!(
headers,
"X-Error-Description",
json_err.to_string().escape_default().collect::<String>()
);
}
AppError::UnknownMethod(method) => {
insert_header!(
headers,
"X-Unknown-Cmd",
method.escape_default().collect::<String>()
);
}
AppError::RequestTooLarge => {
insert_header!(headers, "X-Max-Request-Size", "10 KiB = 10240 bytes");
}
AppError::ApiKeyInvalid { reason } => {
insert_header!(headers, "X-Error-Description", *reason);
}
AppError::QSError(err) => match err {
QSParserError::ParsingError(desc) => {
insert_header!(
headers,
"X-Error-Description",
desc.escape_default().to_string()
);
}
_ => {}
},
_ => {}
};
if cfg!(debug_assertions) {
insert_header!(headers, "X-Full-Error", error_as_string);
}
resp
}
}