Files
asd-backend/src/error.rs

185 lines
5.6 KiB
Rust

use actix_web::{ResponseError, http::StatusCode, HttpResponse};
use serde::Serialize;
use std::fmt;
#[derive(Debug, Serialize)]
pub struct ErrorResponse<T = ()> {
pub success: bool,
pub data: Option<T>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
}
impl<T> ErrorResponse<T> {
pub fn success(data: T) -> Self {
Self {
success: true,
data: Some(data),
error: None,
}
}
pub fn error(msg: impl Into<String>) -> Self {
Self {
success: false,
data: None,
error: Some(msg.into()),
}
}
}
impl<T: Serialize> ErrorResponse<T> {
pub fn to_json_response(self) -> HttpResponse {
HttpResponse::Ok().json(self)
}
}
impl ErrorResponse<()> {
pub fn to_error_response(&self, status: StatusCode) -> HttpResponse {
let body = serde_json::json!({
"success": false,
"error": self.error.clone().unwrap_or_else(|| "Unknown error".to_string())
});
HttpResponse::build(status).json(body)
}
}
#[derive(Debug)]
pub enum AppError {
Unauthorized(String),
Forbidden(String),
NotFound(String),
BadRequest(String),
Internal(String),
Database(String),
TooManyRequests(String),
ServiceUnavailable(String),
}
impl fmt::Display for AppError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AppError::Unauthorized(msg) => write!(f, "{}", msg),
AppError::Forbidden(msg) => write!(f, "{}", msg),
AppError::NotFound(msg) => write!(f, "{}", msg),
AppError::BadRequest(msg) => write!(f, "{}", msg),
AppError::Internal(msg) => write!(f, "{}", msg),
AppError::Database(msg) => write!(f, "{}", msg),
AppError::TooManyRequests(msg) => write!(f, "{}", msg),
AppError::ServiceUnavailable(msg) => write!(f, "{}", msg),
}
}
}
impl ResponseError for AppError {
fn status_code(&self) -> StatusCode {
match self {
AppError::Unauthorized(_) => StatusCode::UNAUTHORIZED,
AppError::Forbidden(_) => StatusCode::FORBIDDEN,
AppError::NotFound(_) => StatusCode::NOT_FOUND,
AppError::BadRequest(_) => StatusCode::BAD_REQUEST,
AppError::Internal(_) | AppError::Database(_) => StatusCode::INTERNAL_SERVER_ERROR,
AppError::TooManyRequests(_) => StatusCode::TOO_MANY_REQUESTS,
AppError::ServiceUnavailable(_) => StatusCode::SERVICE_UNAVAILABLE,
}
}
fn error_response(&self) -> actix_web::HttpResponse {
let error_message = self.to_string();
let status_code = self.status_code().as_u16();
let html = format!(r#"<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>错误 {status_code}</title>
<style>
* {{ margin: 0; padding: 0; box-sizing: border-box; }}
body {{
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, #e74c3c 0%, #c0392b 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}}
.container {{
background: white;
border-radius: 16px;
padding: 48px;
max-width: 480px;
width: 100%;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
text-align: center;
}}
.error-code {{
font-size: 72px;
font-weight: bold;
color: #e74c3c;
margin-bottom: 16px;
}}
h1 {{
color: #333;
margin-bottom: 16px;
font-size: 24px;
}}
.message {{
background: #f8f9fa;
border-radius: 8px;
padding: 16px;
margin: 24px 0;
color: #555;
font-size: 14px;
line-height: 1.6;
}}
.hint {{
color: #888;
font-size: 13px;
margin-top: 24px;
}}
.back-link {{
display: inline-block;
margin-top: 24px;
padding: 12px 24px;
background: #3498db;
color: white;
text-decoration: none;
border-radius: 8px;
font-size: 14px;
transition: background 0.3s;
}}
.back-link:hover {{
background: #2980b9;
}}
</style>
</head>
<body>
<div class="container">
<div class="error-code">{status_code}</div>
<h1>{error_title}</h1>
<div class="message">{error_message}</div>
<p style="margin-top: 24px; color: #3498db; font-size: 14px;">请返回小程序重新生成新的下载链接</p>
<div class="hint">如果问题持续存在,请联系技术支持</div>
</div>
</body>
</html>"#,
error_title = match self {
AppError::Unauthorized(_) => "认证失败",
AppError::Forbidden(_) => "权限不足",
AppError::NotFound(_) => "内容未找到",
AppError::BadRequest(_) => "请求无效",
AppError::Internal(_) | AppError::Database(_) => "服务器错误",
AppError::TooManyRequests(_) => "请求过于频繁",
AppError::ServiceUnavailable(_) => "服务暂不可用",
}
);
HttpResponse::build(self.status_code())
.content_type("text/html; charset=utf-8")
.body(html)
}
}