Skip to main content

salvo_oapi/extract/
payload.rs

1//! Request body extractors for the API operation.
2
3pub use salvo_core::extract::{FormBody, FormFile, FormFiles, JsonBody};
4use serde::Deserialize;
5
6use crate::endpoint::EndpointArgRegister;
7use crate::{
8    Array, BasicType, Components, Content, KnownFormat, Object, Operation, RequestBody, Schema,
9    SchemaFormat, ToRequestBody, ToSchema,
10};
11
12impl EndpointArgRegister for FormFile {
13    fn register(_components: &mut Components, operation: &mut Operation, arg: &str) {
14        let schema = Schema::from(
15            Object::new().property(
16                arg,
17                Object::with_type(BasicType::String)
18                    .format(SchemaFormat::KnownFormat(KnownFormat::Binary)),
19            ),
20        );
21
22        if let Some(request_body) = &mut operation.request_body {
23            request_body
24                .contents
25                .insert("multipart/form-data".into(), Content::new(schema));
26        } else {
27            let request_body = RequestBody::new()
28                .description("Upload a file.")
29                .add_content("multipart/form-data", Content::new(schema));
30            operation.request_body = Some(request_body);
31        }
32    }
33}
34
35impl EndpointArgRegister for FormFiles {
36    fn register(_components: &mut Components, operation: &mut Operation, arg: &str) {
37        let schema = Schema::from(
38            Object::new().property(
39                arg,
40                Array::new().items(Schema::from(
41                    Object::with_type(BasicType::String)
42                        .format(SchemaFormat::KnownFormat(KnownFormat::Binary)),
43                )),
44            ),
45        );
46        if let Some(request_body) = &mut operation.request_body {
47            request_body
48                .contents
49                .insert("multipart/form-data".into(), Content::new(schema));
50        } else {
51            let request_body = RequestBody::new()
52                .description("Upload files.")
53                .add_content("multipart/form-data", Content::new(schema));
54            operation.request_body = Some(request_body);
55        }
56    }
57}
58
59impl<'de, T> ToRequestBody for FormBody<T>
60where
61    T: Deserialize<'de> + ToSchema,
62{
63    fn to_request_body(components: &mut Components) -> RequestBody {
64        let schema = T::to_schema(components);
65        RequestBody::new()
66            .description("Extract form format data from request.")
67            .add_content(
68                "application/x-www-form-urlencoded",
69                Content::new(schema.clone()),
70            )
71            // Keep the form schema separate from the file schema registered under
72            // `multipart/form-data` until those schemas can be merged correctly.
73            .add_content("multipart/*", Content::new(schema))
74    }
75}
76
77impl<'de, T> EndpointArgRegister for FormBody<T>
78where
79    T: Deserialize<'de> + ToSchema,
80{
81    fn register(components: &mut Components, operation: &mut Operation, _arg: &str) {
82        operation.request_body = Some(Self::to_request_body(components));
83    }
84}
85
86impl<'de, T> ToRequestBody for JsonBody<T>
87where
88    T: Deserialize<'de> + ToSchema,
89{
90    fn to_request_body(components: &mut Components) -> RequestBody {
91        RequestBody::new()
92            .description("Extract json format data from request.")
93            .add_content("application/json", Content::new(T::to_schema(components)))
94    }
95}
96
97impl<'de, T> EndpointArgRegister for JsonBody<T>
98where
99    T: Deserialize<'de> + ToSchema,
100{
101    fn register(components: &mut Components, operation: &mut Operation, _arg: &str) {
102        let request_body = Self::to_request_body(components);
103        let _ = <T as ToSchema>::to_schema(components);
104        operation.request_body = Some(request_body);
105    }
106}
107
108#[cfg(test)]
109mod tests {
110    use assert_json_diff::assert_json_eq;
111    use serde_json::json;
112
113    use super::*;
114
115    #[test]
116    fn test_form_body_to_request_body() {
117        let mut components = Components::default();
118        let request_body = FormBody::<String>::to_request_body(&mut components);
119        assert_json_eq!(
120            request_body,
121            json!({
122                "description": "Extract form format data from request.",
123                "content": {
124                    "application/x-www-form-urlencoded": {
125                        "schema": { "type": "string" }
126                    },
127                    "multipart/*": {
128                        "schema": { "type": "string" }
129                    }
130                }
131            })
132        );
133    }
134
135    #[test]
136    fn test_form_body_register() {
137        let mut components = Components::new();
138        let mut operation = Operation::new();
139        FormBody::<String>::register(&mut components, &mut operation, "arg");
140
141        assert_json_eq!(
142            operation,
143            json!({
144                "requestBody": {
145                    "content": {
146                        "application/x-www-form-urlencoded": {
147                            "schema": { "type": "string" }
148                        },
149                        "multipart/*": {
150                            "schema": { "type": "string" }
151                        }
152                    },
153                    "description": "Extract form format data from request."
154                },
155                "responses": {}
156            })
157        );
158    }
159
160    #[test]
161    fn test_json_body_to_request_body() {
162        let mut components = Components::default();
163        let request_body = JsonBody::<String>::to_request_body(&mut components);
164        assert_json_eq!(
165            request_body,
166            json!({
167                "description": "Extract json format data from request.",
168                "content": {
169                    "application/json": {
170                        "schema": { "type": "string" }
171                    }
172                }
173            })
174        );
175    }
176
177    #[test]
178    fn test_json_body_register() {
179        let mut components = Components::new();
180        let mut operation = Operation::new();
181        JsonBody::<String>::register(&mut components, &mut operation, "arg");
182
183        assert_json_eq!(
184            operation,
185            json!({
186                "requestBody": {
187                    "content": {
188                        "application/json": {
189                            "schema": { "type": "string" }
190                        }
191                    },
192                    "description": "Extract json format data from request."
193                },
194                "responses": {}
195            })
196        );
197    }
198}