Skip to main content

salvo_core/writing/
json.rs

1use std::fmt::{self, Debug, Display, Formatter};
2
3use async_trait::async_trait;
4use serde::Serialize;
5
6use super::{Scribe, try_set_header};
7use crate::http::header::{CONTENT_TYPE, HeaderValue};
8use crate::http::{Response, StatusError};
9
10/// Write serializable content to response as json content.
11///
12/// It will set `content-type` to `application/json; charset=utf-8`.
13///
14/// # Example
15///
16/// ```
17/// use salvo_core::prelude::*;
18/// use serde::Serialize;
19///
20/// #[derive(Serialize)]
21/// struct User {
22///     name: String,
23/// }
24/// #[handler]
25/// async fn hello(res: &mut Response) -> Json<User> {
26///     Json(User {
27///         name: "jobs".into(),
28///     })
29/// }
30/// ```
31pub struct Json<T>(pub T);
32
33#[async_trait]
34impl<T> Scribe for Json<T>
35where
36    T: Serialize + Send,
37{
38    fn render(self, res: &mut Response) {
39        match serde_json::to_vec(&self.0) {
40            Ok(bytes) => {
41                try_set_header(
42                    &mut res.headers,
43                    CONTENT_TYPE,
44                    HeaderValue::from_static("application/json; charset=utf-8"),
45                );
46                let _ = res.write_body(bytes);
47            }
48            Err(e) => {
49                tracing::error!(error = ?e, "JsonContent write error");
50                res.render(StatusError::internal_server_error());
51            }
52        }
53    }
54}
55impl<T: Debug> Debug for Json<T> {
56    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
57        f.debug_tuple("Json").field(&self.0).finish()
58    }
59}
60impl<T: Display> Display for Json<T> {
61    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
62        Display::fmt(&self.0, f)
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69    use crate::prelude::*;
70    use crate::test::{ResponseExt, TestClient};
71
72    #[tokio::test]
73    async fn test_write_json_content() {
74        #[derive(Serialize, Debug)]
75        struct User {
76            name: String,
77        }
78        #[handler]
79        async fn test() -> Json<User> {
80            Json(User {
81                name: "jobs".into(),
82            })
83        }
84
85        let router = Router::new().push(Router::with_path("test").get(test));
86        let mut res = TestClient::get("http://127.0.0.1:8698/test")
87            .send(router)
88            .await;
89        assert_eq!(res.take_string().await.unwrap(), r#"{"name":"jobs"}"#);
90        assert_eq!(
91            res.headers().get("content-type").unwrap(),
92            "application/json; charset=utf-8"
93        );
94    }
95}