rustbasic_core/
requests.rs1use axum::{
2 extract::{FromRequest, FromRequestParts, Query, Form, Json, Request as AxumRequest},
3 http::Method,
4 response::{IntoResponse, Response},
5};
6use serde_json::{json, Value};
7use std::collections::HashMap;
8use validator::Validate;
9use axum_session::Session;
10use crate::session_manager::RustBasicSessionStore;
11
12pub struct Request {
13 pub inputs: Value,
14 pub method: Method,
15 pub path: String,
16 pub headers: HashMap<String, String>,
17 pub session: Session<RustBasicSessionStore>,
18}
19
20impl Request {
21 #[allow(dead_code)]
22 pub fn input(&self, key: &str) -> Option<&Value> {
23 self.inputs.get(key)
24 }
25
26 pub fn input_as_str(&self, key: &str) -> Option<&str> {
27 self.inputs.get(key).and_then(|v| v.as_str())
28 }
29
30 pub fn query(&self, key: &str) -> Option<&str> {
31 self.input_as_str(key)
32 }
33
34 pub fn all(&self) -> &Value {
35 &self.inputs
36 }
37
38 pub fn validate<T: Validate + serde::de::DeserializeOwned>(&self) -> Result<T, Box<(axum::http::StatusCode, Response)>> {
39 let data: T = serde_json::from_value(self.inputs.clone()).map_err(|e| {
40 Box::new((axum::http::StatusCode::UNPROCESSABLE_ENTITY,
41 axum::response::Json(json!({ "error": "Invalid format", "detail": e.to_string() })).into_response()))
42 })?;
43
44 data.validate().map_err(|e| {
45 self.session.set("old", self.inputs.clone());
47
48 Box::new((axum::http::StatusCode::UNPROCESSABLE_ENTITY,
49 axum::response::Json(json!({ "errors": e })).into_response()))
50 })?;
51
52 Ok(data)
53 }
54}
55
56impl<S> FromRequest<S> for Request
57where
58 S: Send + Sync,
59{
60 type Rejection = Response;
61
62 async fn from_request(req: AxumRequest, state: &S) -> Result<Self, Self::Rejection> {
63 let method = req.method().clone();
64 let mut inputs = json!({});
65
66 let (mut parts, body) = req.into_parts();
68 if let Ok(Query(query_params)) = Query::<HashMap<String, String>>::from_request_parts(&mut parts, state).await {
69 for (k, v) in query_params {
70 inputs[k] = json!(v);
71 }
72 }
73
74 let parts_copy = parts.clone();
76 let content_type = parts.headers.get(axum::http::header::CONTENT_TYPE)
77 .and_then(|v| v.to_str().ok())
78 .unwrap_or("");
79
80 if method == Method::POST || method == Method::PUT || method == Method::PATCH {
81 if content_type.starts_with("application/json") {
82 if let Ok(Json(json_data)) = Json::<Value>::from_request(axum::http::Request::from_parts(parts_copy, body), state).await {
83 if let Value::Object(obj) = json_data {
84 for (k, v) in obj {
85 inputs[k] = v;
86 }
87 }
88 }
89 } else if let Ok(Form(form_data)) = Form::<HashMap<String, String>>::from_request(axum::http::Request::from_parts(parts_copy, body), state).await {
90 for (k, v) in form_data {
91 inputs[k] = json!(v);
92 }
93 }
94 }
95
96 let session = parts.extensions
98 .get::<Session<RustBasicSessionStore>>()
99 .cloned()
100 .ok_or_else(|| (axum::http::StatusCode::INTERNAL_SERVER_ERROR, "Session tidak ditemukan").into_response())?;
101
102 let path = parts.uri.path().to_string();
103 let mut headers = HashMap::new();
104 for (name, value) in parts.headers.iter() {
105 if let Ok(val_str) = value.to_str() {
106 headers.insert(name.as_str().to_lowercase(), val_str.to_string());
107 }
108 }
109
110 Ok(Request {
111 inputs,
112 method,
113 path,
114 headers,
115 session,
116 })
117 }
118}