salvo_core/writing/
json.rs1use 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
10pub 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}