use crate::axum::{
Json,
extract::{FromRequest, Request, rejection::JsonRejection},
http::StatusCode,
response::{IntoResponse, Response},
};
use crate::serde::de::DeserializeOwned;
use crate::{serde_json, tracing};
pub struct LoggedJson<T>(pub T);
impl<S, T> FromRequest<S> for LoggedJson<T>
where
T: DeserializeOwned,
S: Send + Sync,
{
type Rejection = Response;
async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
match Json::<T>::from_request(req, state).await {
Ok(Json(value)) => Ok(LoggedJson(value)),
Err(rejection) => {
let error_message = format!("JSON deserialization failed: {:?}", rejection);
tracing::error!("{}", error_message);
let response = (
StatusCode::UNPROCESSABLE_ENTITY,
Json(serde_json::json!({
"error": "JSON deserialization failed",
"message": error_message,
})),
)
.into_response();
Err(response)
}
}
}
}
pub struct DetailedJson<T>(pub T);
impl<S, T> FromRequest<S> for DetailedJson<T>
where
T: DeserializeOwned,
S: Send + Sync,
{
type Rejection = Response;
async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
match Json::<T>::from_request(req, state).await {
Ok(Json(value)) => Ok(DetailedJson(value)),
Err(rejection) => {
let (status, error_type, message) = match rejection {
JsonRejection::JsonDataError(err) => (
StatusCode::UNPROCESSABLE_ENTITY,
"json_data_error",
format!("Invalid JSON data: {}", err),
),
JsonRejection::JsonSyntaxError(err) => (
StatusCode::BAD_REQUEST,
"json_syntax_error",
format!("JSON syntax error: {}", err),
),
JsonRejection::MissingJsonContentType(err) => (
StatusCode::UNSUPPORTED_MEDIA_TYPE,
"missing_content_type",
format!("Missing Content-Type: application/json header: {}", err),
),
JsonRejection::BytesRejection(err) => (
StatusCode::INTERNAL_SERVER_ERROR,
"bytes_rejection",
format!("Failed to read request body: {}", err),
),
_ => (
StatusCode::BAD_REQUEST,
"unknown_error",
format!("Unknown error: {:?}", rejection),
),
};
tracing::error!("JSON extraction failed [{}]: {}", error_type, message);
let response = (
status,
Json(serde_json::json!({
"error": error_type,
"message": message,
"status": status.as_u16(),
})),
)
.into_response();
Err(response)
}
}
}
}