145 lines
4.1 KiB
Rust
145 lines
4.1 KiB
Rust
use anyhow::Error;
|
|
use axum::{
|
|
Json,
|
|
http::StatusCode,
|
|
response::IntoResponse,
|
|
extract::rejection::{
|
|
QueryRejection,
|
|
PathRejection,
|
|
JsonRejection,
|
|
FormRejection,
|
|
},
|
|
};
|
|
use serde::Serialize;
|
|
use serde_json::Value;
|
|
use sqlx::Error as SqlxError;
|
|
|
|
#[derive(Debug, Serialize)]
|
|
pub struct ErrResp {
|
|
pub err_code: i32,
|
|
pub err_msg: String,
|
|
pub err_detail: Option<Value>,
|
|
}
|
|
|
|
impl ErrResp {
|
|
pub fn new(err_code: i32, err_msg: impl Into<String>, detail: Option<Value>) -> Self {
|
|
Self {
|
|
err_code,
|
|
err_msg: err_msg.into(),
|
|
err_detail: detail,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
#[allow(dead_code)]
|
|
pub enum ApiErr {
|
|
Unauthorized(String, Option<Value>),
|
|
Forbidden(String, Option<Value>),
|
|
BadRequest(String, Option<Value>),
|
|
NotFound(String, Option<Value>),
|
|
Internal(String, Option<Value>),
|
|
}
|
|
|
|
impl IntoResponse for ApiErr {
|
|
fn into_response(self) -> axum::response::Response {
|
|
match self {
|
|
ApiErr::Unauthorized(msg, detail) => {
|
|
(StatusCode::UNAUTHORIZED, Json(ErrResp::new(401, msg, detail))).into_response()
|
|
}
|
|
ApiErr::Forbidden(msg, detail) => {
|
|
(StatusCode::FORBIDDEN, Json(ErrResp::new(403, msg, detail))).into_response()
|
|
}
|
|
ApiErr::BadRequest(msg, detail) => {
|
|
(StatusCode::BAD_REQUEST, Json(ErrResp::new(400, msg, detail))).into_response()
|
|
}
|
|
ApiErr::NotFound(msg, detail) => {
|
|
(StatusCode::NOT_FOUND, Json(ErrResp::new(404, msg, detail))).into_response()
|
|
}
|
|
ApiErr::Internal(msg, detail) => (
|
|
StatusCode::INTERNAL_SERVER_ERROR,
|
|
Json(ErrResp::new(500, msg, detail)),
|
|
)
|
|
.into_response(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<Error> for ApiErr {
|
|
fn from(err: Error) -> Self {
|
|
tracing::error!("Error: {:?}; root_cause: {}", err, err.root_cause());
|
|
ApiErr::Internal("internal server error".to_string(), None)
|
|
}
|
|
}
|
|
|
|
impl From<SqlxError> for ApiErr {
|
|
fn from(err: SqlxError) -> Self {
|
|
match err {
|
|
SqlxError::RowNotFound => {
|
|
ApiErr::NotFound("Resource not found".into(), None)
|
|
}
|
|
SqlxError::Database(db_err) => {
|
|
if db_err.code().as_deref() == Some("23505") {
|
|
ApiErr::BadRequest("数据已存在".into(), None)
|
|
} else {
|
|
tracing::error!("Database error: {}", db_err);
|
|
ApiErr::Internal("Database error".into(), None)
|
|
}
|
|
}
|
|
_ => {
|
|
tracing::error!("Database error: {}", err);
|
|
ApiErr::Internal("Database error".into(), None)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<QueryRejection> for ApiErr {
|
|
fn from(rejection: QueryRejection) -> Self {
|
|
tracing::warn!("Query parameter error: {}", rejection);
|
|
ApiErr::BadRequest(
|
|
"Invalid query parameters".to_string(),
|
|
Some(serde_json::json!({
|
|
"detail": rejection.to_string()
|
|
}))
|
|
)
|
|
}
|
|
}
|
|
|
|
impl From<PathRejection> for ApiErr {
|
|
fn from(rejection: PathRejection) -> Self {
|
|
tracing::warn!("Path parameter error: {}", rejection);
|
|
ApiErr::BadRequest(
|
|
"Invalid path parameter".to_string(),
|
|
Some(serde_json::json!({
|
|
"detail": rejection.to_string()
|
|
}))
|
|
)
|
|
}
|
|
}
|
|
|
|
impl From<JsonRejection> for ApiErr {
|
|
fn from(rejection: JsonRejection) -> Self {
|
|
tracing::warn!("JSON parsing error: {}", rejection);
|
|
ApiErr::BadRequest(
|
|
"Invalid JSON format".to_string(),
|
|
Some(serde_json::json!({
|
|
"detail": rejection.to_string()
|
|
}))
|
|
)
|
|
}
|
|
}
|
|
|
|
impl From<FormRejection> for ApiErr {
|
|
fn from(rejection: FormRejection) -> Self {
|
|
tracing::warn!("Form data error: {}", rejection);
|
|
ApiErr::BadRequest(
|
|
"Invalid form data".to_string(),
|
|
Some(serde_json::json!({
|
|
"detail": rejection.to_string()
|
|
}))
|
|
)
|
|
}
|
|
}
|
|
|