Skip to main content

nimble_http/
response.rs

1//! response.rs
2
3use hyper::{Body, Response, StatusCode, header};
4use mime_guess::from_path as get_mine_from_path;
5use serde::Serialize;
6use std::fs::read_to_string as read_file;
7
8/// 转换为 HTTP 响应的 trait
9pub trait IntoResponse {
10	fn into_response(self) -> Response<Body>;
11}
12
13// 为 &str 实现
14impl IntoResponse for &'static str {
15	fn into_response(self) -> Response<Body> {
16		Response::builder()
17			.header(header::CONTENT_TYPE, "text/plain; charset=utf-8")
18			.body(Body::from(self))
19			.unwrap()
20	}
21}
22
23// 为 String 实现
24impl IntoResponse for String {
25	fn into_response(self) -> Response<Body> {
26		Response::builder()
27			.header(header::CONTENT_TYPE, "text/plain; charset=utf-8")
28			.body(Body::from(self))
29			.unwrap()
30	}
31}
32
33// 为 Vec<u8> 实现
34impl IntoResponse for Vec<u8> {
35	fn into_response(self) -> Response<Body> {
36		Response::builder().body(Body::from(self)).unwrap()
37	}
38}
39
40// 为 () 实现(空响应)
41impl IntoResponse for () {
42	fn into_response(self) -> Response<Body> {
43		Response::builder()
44			.status(StatusCode::NO_CONTENT)
45			.body(Body::empty())
46			.unwrap()
47	}
48}
49
50// 为 StatusCode 实现(仅状态码)
51impl IntoResponse for StatusCode {
52	fn into_response(self) -> Response<Body> {
53		Response::builder()
54			.status(self)
55			.body(Body::empty())
56			.unwrap()
57	}
58}
59
60/// JSON 响应包装器
61pub struct Json<T: Serialize>(pub T);
62
63impl<T: Serialize> IntoResponse for Json<T> {
64	fn into_response(self) -> Response<Body> {
65		let json = serde_json::to_string(&self.0).unwrap();
66		Response::builder()
67			.header(header::CONTENT_TYPE, "application/json")
68			.body(Body::from(json))
69			.unwrap()
70	}
71}
72
73/// HTML 响应包装器
74pub struct Html(pub String);
75
76impl IntoResponse for Html {
77	fn into_response(self) -> Response<Body> {
78		Response::builder()
79			.header(header::CONTENT_TYPE, "text/html; charset=utf-8")
80			.body(Body::from(self.0))
81			.unwrap()
82	}
83}
84
85/// 文本响应包装器
86pub struct Text(pub String);
87
88impl IntoResponse for Text {
89	fn into_response(self) -> Response<Body> {
90		Response::builder()
91			.header(header::CONTENT_TYPE, "text/plain; charset=utf-8")
92			.body(Body::from(self.0))
93			.unwrap()
94	}
95}
96
97// 为 Result 实现
98impl<T, E> IntoResponse for Result<T, E>
99where
100	T: IntoResponse,
101	E: IntoResponse,
102{
103	fn into_response(self) -> Response<Body> {
104		match self {
105			Ok(value) => value.into_response(),
106			Err(err) => err.into_response(),
107		}
108	}
109}
110
111// 重定向url包装器
112pub struct Redirect(pub String);
113
114impl IntoResponse for Redirect {
115	fn into_response(self) -> Response<Body> {
116        Response::builder()
117            .status(302)
118            .header("Location", self.0)
119            .body(Body::empty())
120            .unwrap()
121	}
122}
123
124// 永久重定向url包装器
125pub struct PermRedirect(pub String);
126
127impl IntoResponse for PermRedirect {
128    fn into_response(self) -> Response<Body> {
129        Response::builder()
130            .status(301)
131            .header("Location", self.0)
132            .body(Body::empty())
133            .unwrap()
134    }
135}
136
137impl Redirect {
138	pub fn perm(url: String) -> PermRedirect {
139		PermRedirect(url)
140	}
141}
142
143// 文件包装器
144pub struct File(pub String, pub bool);
145
146impl IntoResponse for File {
147	fn into_response(self) -> Response<Body> {
148		Response::builder()
149			.header(
150				header::CONTENT_TYPE,
151				if self.1 {
152					"application/octet-stream; charset=utf-8".to_string()
153				} else {
154					format!(
155						"{}; charset=utf-8",
156						get_mine_from_path(&self.0)
157							.first_or_octet_stream()
158							.to_string()
159					)
160				},
161			)
162			.body(Body::from(read_file(self.0).unwrap_or(String::new())))
163			.unwrap()
164	}
165}