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, min: Option, }, #[display(fmt = "IDK")] UnknownBody { json_err: Option, query_error: Option, }, } 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::() ); } AppError::UnknownMethod(method) => { insert_header!( headers, "X-Unknown-Cmd", method.escape_default().collect::() ); } 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 } }