Skip to main content

platform_http/
json.rs

1use crate::{ApiErrorResponse, HttpRequestContext};
2use axum::extract::{FromRequest, Json, Request};
3use platform_core::AppError;
4use platform_core::error::ErrorDetail;
5use serde::de::DeserializeOwned;
6
7#[derive(Debug, Clone, Copy, Default)]
8pub struct JsonBody<T>(pub T);
9
10impl<S, T> FromRequest<S> for JsonBody<T>
11where
12    S: Send + Sync,
13    T: DeserializeOwned,
14{
15    type Rejection = ApiErrorResponse;
16
17    async fn from_request(request: Request, state: &S) -> Result<Self, Self::Rejection> {
18        let context = request.extensions().get::<HttpRequestContext>().cloned();
19
20        Json::<T>::from_request(request, state)
21            .await
22            .map(|Json(value)| Self(value))
23            .map_err(|rejection| {
24                let error = AppError::validation(
25                    "Request validation failed",
26                    vec![ErrorDetail {
27                        field: None,
28                        reason: json_rejection_reason(&rejection).to_owned(),
29                    }],
30                );
31
32                match context {
33                    Some(ctx) => ApiErrorResponse::with_context(error, &ctx),
34                    None => error.into(),
35                }
36            })
37    }
38}
39
40fn json_rejection_reason(rejection: &axum::extract::rejection::JsonRejection) -> &'static str {
41    match rejection {
42        axum::extract::rejection::JsonRejection::JsonDataError(_) => {
43            "Request body contains invalid JSON data"
44        }
45        axum::extract::rejection::JsonRejection::JsonSyntaxError(_) => {
46            "Request body contains malformed JSON"
47        }
48        axum::extract::rejection::JsonRejection::MissingJsonContentType(_) => {
49            "Request body must be JSON"
50        }
51        axum::extract::rejection::JsonRejection::BytesRejection(_) => {
52            "Request body could not be read"
53        }
54        _ => "Request body is invalid",
55    }
56}