rust_qcos/
request.rs

1//! 请求封装
2use std::collections::HashMap;
3use std::fmt::Display;
4
5use bytes::Bytes;
6
7use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
8use reqwest::Body;
9use serde_json::value::Value;
10use std::convert::From;
11use std::str::FromStr;
12use std::time::Duration;
13
14use reqwest;
15
16pub struct Request;
17use serde;
18
19#[derive(Debug, serde::Serialize, serde::Deserialize, PartialEq)]
20pub struct InitiateMultipartUploadResult {
21    #[serde(rename(deserialize = "Bucket"))]
22    bucket: String,
23    #[serde(rename(deserialize = "Key"))]
24    key: String,
25    #[serde(rename(deserialize = "UploadId"))]
26    pub upload_id: String,
27}
28
29/// ```
30/// use rust_qcos::request::{CompleteMultipartUpload, Part};
31/// use quick_xml::se::to_string;
32/// let objs = CompleteMultipartUpload{part:vec![Part{part_number: 1, etag: "abc".to_string()}, Part{part_number: 2, etag: "abc".to_string()}]};
33/// let s = to_string(&objs).unwrap();
34/// assert_eq!(s, r#"<CompleteMultipartUpload><Part><PartNumber>1</PartNumber><ETag>abc</ETag></Part><Part><PartNumber>2</PartNumber><ETag>abc</ETag></Part></CompleteMultipartUpload>"#)
35/// ```
36#[derive(Debug, serde::Serialize, serde::Deserialize)]
37pub struct CompleteMultipartUpload {
38    #[serde(rename(serialize = "Part"))]
39    pub part: Vec<Part>,
40}
41
42#[derive(Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq, Clone)]
43pub struct Part {
44    #[serde(rename = "$unflatten=PartNumber")]
45    pub part_number: u64,
46    #[serde(rename = "$unflatten=ETag")]
47    pub etag: String,
48}
49
50/// 错误码
51#[derive(Debug, PartialEq)]
52pub enum ErrNo {
53    /// 操作成功
54    SUCCESS = 0,
55    /// 其他错误
56    OTHER = 10000,
57    /// http status code 相关错误
58    STATUS = 10001,
59    /// 解码相关错误
60    DECODE = 10002,
61    /// 连接相关错误
62    CONNECT = 10003,
63    /// 编码相关错误
64    ENCODE = 20001,
65    /// IO错误
66    IO = 20002,
67}
68
69/// 请求方法
70#[derive(Debug, Eq, PartialEq)]
71pub enum Method {
72    Get,
73    Post,
74    Delete,
75    Put,
76    Head,
77}
78
79/// # Examples
80/// ```
81/// use rust_qcos::request::ErrNo;
82/// println!("{:#?}", ErrNo::OTHER);
83/// ```
84impl std::fmt::Display for ErrNo {
85    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86        write!(f, "{:#?}", self)
87    }
88}
89
90/// http请求返回类型,无论成功还是失败都返回该类型,根据`error_no`可区分是否成功
91#[derive(Debug)]
92pub struct Response {
93    /// 错误码
94    pub error_no: ErrNo,
95    /// 错误信息
96    pub error_message: String,
97    /// 接口返回信息,当接口返回错误时也可能有值
98    pub result: Bytes,
99    /// 接口返回的headers, 有些接口需要拿到头部信息进行校验
100    pub headers: HashMap<String, String>,
101}
102
103impl From<reqwest::Error> for Response {
104    fn from(value: reqwest::Error) -> Self {
105        let mut e = ErrNo::OTHER;
106        if value.is_status() {
107            e = ErrNo::STATUS;
108        } else if value.is_connect() {
109            e = ErrNo::CONNECT;
110        } else if value.is_decode() {
111            e = ErrNo::DECODE;
112        }
113        Response {
114            error_no: e,
115            error_message: value.to_string(),
116            result: Bytes::from(""),
117            headers: HashMap::new(),
118        }
119    }
120}
121
122impl Display for Response {
123    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
124        write!(
125            f,
126            r#"{{"error_no": "{}","error_message": "{}","result": "{}"}}"#,
127            self.error_no,
128            self.error_message,
129            String::from_utf8_lossy(&self.result[..])
130        )
131    }
132}
133
134impl Response {
135    pub fn new(error_no: ErrNo, error_message: String, result: String) -> Self {
136        Self {
137            error_no,
138            error_message,
139            result: Bytes::from(result),
140            headers: HashMap::new(),
141        }
142    }
143    /// 生成一个空的成功的`Response`对象
144    pub fn blank_success() -> Self {
145        Self::new(ErrNo::SUCCESS, "".to_string(), "".to_string())
146    }
147}
148
149type Data = Value;
150
151/// 请求封装类
152impl Request {
153    /// 从传入的`headers`参数生成`reqwest::blocking::ClientBuilder`
154    fn get_builder_with_headers(
155        headers: Option<&HashMap<String, String>>,
156    ) -> reqwest::ClientBuilder {
157        let mut builder = reqwest::ClientBuilder::new();
158        if let Some(headers) = headers {
159            let mut header = HeaderMap::new();
160            for (k, v) in headers {
161                header.insert(
162                    HeaderName::from_str(k).unwrap(),
163                    HeaderValue::from_str(v).unwrap(),
164                );
165            }
166            builder = builder.default_headers(header);
167        }
168        builder
169    }
170    /// send Head request
171    /// # Examples
172    /// ```
173    /// use rust_qcos::request::Request;
174    /// use std::collections::HashMap;
175    /// async {
176    /// let mut headers = HashMap::new();
177    /// headers.insert("x-test-header".to_string(), "test-header".to_string());
178    /// Request::head("https://www.baiduc.com", None, Some(&headers)).await;
179    /// };
180    /// ```
181    pub async fn head(
182        url: &str,
183        query: Option<&HashMap<String, String>>,
184        headers: Option<&HashMap<String, String>>,
185    ) -> Result<Response, Response> {
186        Request::do_req(
187            Method::Head,
188            url,
189            query,
190            headers,
191            None,
192            None,
193            None as Option<Body>,
194        )
195        .await
196    }
197    /// send get request
198    /// # Examples
199    /// ```
200    /// use rust_qcos::request::Request;
201    /// use std::collections::HashMap;
202    /// async {
203    /// let mut headers = HashMap::new();
204    /// headers.insert("x-test-header".to_string(), "test-header".to_string());
205    /// Request::get("https://www.baiduc.com", None, Some(&headers)).await;
206    /// };
207    /// ```
208    pub async fn get(
209        url: &str,
210        query: Option<&HashMap<String, String>>,
211        headers: Option<&HashMap<String, String>>,
212    ) -> Result<Response, Response> {
213        Request::do_req(
214            Method::Get,
215            url,
216            query,
217            headers,
218            None,
219            None,
220            None as Option<Body>,
221        )
222        .await
223    }
224    /// send post request
225    /// # Examples
226    /// ```
227    /// use reqwest::Body;
228    /// use rust_qcos::request::Request;
229    /// use std::collections::HashMap;
230    /// use serde_json::json;
231    /// async {
232    /// let mut form = HashMap::new();
233    /// form.insert("hello", json!(1i16));
234    /// form.insert("hello1", json!("world"));
235    /// let mut json = HashMap::new();
236    /// json.insert("hello", json!(1i64));
237    /// json.insert("hello_json", json!("world"));
238    /// json.insert("data", json!(vec![1u8, 2u8, 3u8] as Vec<u8>));
239    /// let resp = Request::post(
240    ///     "https://www.baidu.com",
241    ///     None,
242    ///     None,
243    ///     Some(&form),
244    ///     Some(&json),
245    ///     None as Option<Body>,
246    /// ).await;
247    /// };
248    /// ```
249    pub async fn post<T: Into<Body>>(
250        url: &str,
251        query: Option<&HashMap<String, String>>,
252        headers: Option<&HashMap<String, String>>,
253        form: Option<&HashMap<&str, Data>>,
254        json: Option<&HashMap<&str, Data>>,
255        body_data: Option<T>,
256    ) -> Result<Response, Response> {
257        Request::do_req(Method::Post, url, query, headers, form, json, body_data).await
258    }
259
260    /// send put request
261    pub async fn put<T: Into<Body>>(
262        url: &str,
263        query: Option<&HashMap<String, String>>,
264        headers: Option<&HashMap<String, String>>,
265        form: Option<&HashMap<&str, Data>>,
266        json: Option<&HashMap<&str, Data>>,
267        body_data: Option<T>,
268    ) -> Result<Response, Response> {
269        Request::do_req(Method::Put, url, query, headers, form, json, body_data).await
270    }
271
272    /// send delete request
273    pub async fn delete(
274        url: &str,
275        query: Option<&HashMap<String, String>>,
276        headers: Option<&HashMap<String, String>>,
277        form: Option<&HashMap<&str, Data>>,
278        json: Option<&HashMap<&str, Data>>,
279    ) -> Result<Response, Response> {
280        Request::do_req(
281            Method::Delete,
282            url,
283            query,
284            headers,
285            form,
286            json,
287            None as Option<Body>,
288        )
289        .await
290    }
291
292    async fn do_req<T: Into<Body>>(
293        method: Method,
294        url: &str,
295        query: Option<&HashMap<String, String>>,
296        headers: Option<&HashMap<String, String>>,
297        form: Option<&HashMap<&str, Data>>,
298        json: Option<&HashMap<&str, Data>>,
299        body_data: Option<T>,
300    ) -> Result<Response, Response> {
301        let builder = Self::get_builder_with_headers(headers);
302        let client = builder.timeout(Duration::from_secs(24 * 3600)).build()?;
303        let mut req = match method {
304            Method::Get => client.get(url),
305            Method::Delete => client.delete(url),
306            Method::Post => client.post(url),
307            Method::Put => client.put(url),
308            Method::Head => client.head(url),
309        };
310        if let Some(v) = query {
311            req = req.query(v);
312        }
313        if let Some(v) = form {
314            req = req.form(v);
315        }
316        if let Some(v) = json {
317            req = req.json(v);
318        }
319        if let Some(v) = body_data {
320            req = req.body(v.into());
321        }
322        let resp = req.send().await?;
323        let status_code = resp.status();
324        let mut error_no = ErrNo::SUCCESS;
325        let mut message = "".to_string();
326        if status_code.is_client_error() || status_code.is_server_error() {
327            error_no = ErrNo::STATUS;
328            message = format!("{}", status_code);
329        }
330        let mut headers = HashMap::new();
331        for (k, v) in resp.headers() {
332            headers.insert(k.to_string(), String::from_utf8_lossy(v.as_bytes()).into());
333        }
334        Ok(Response {
335            error_no,
336            error_message: message,
337            result: resp.bytes().await?,
338            headers,
339        })
340    }
341}
342
343#[cfg(test)]
344mod tests {
345    use crate::request::{ErrNo, Request};
346    use reqwest::Body;
347    use serde_json::json;
348    use std::collections::HashMap;
349
350    #[tokio::test]
351    async fn test_get() {
352        let mut header = HashMap::new();
353        header.insert("user-agent".to_string(), "test-user-agent".to_string());
354        let mut query = HashMap::new();
355        query.insert("a".to_string(), "a".to_string());
356        query.insert("b".to_string(), "b".to_string());
357        query.insert("c".to_string(), "c".to_string());
358        let response = Request::get("https://www.baidu.com", Some(&query), Some(&header)).await;
359        match response {
360            Ok(e) => {
361                println!("{:#?}", e);
362            }
363            Err(e) => println!("{}", e),
364        }
365    }
366
367    #[tokio::test]
368    async fn test_post_form() {
369        let mut form = HashMap::new();
370        form.insert("hello", json!(1i16));
371        form.insert("hello1", json!("world"));
372        let mut json = HashMap::new();
373        json.insert("hello", json!(1i64));
374        json.insert("hello_json", json!("world"));
375        json.insert("data", json!(vec![1u8, 2u8, 3u8] as Vec<u8>));
376        let resp = Request::post(
377            "https://www.baidu.com",
378            None,
379            None,
380            Some(&form),
381            Some(&json),
382            None as Option<Body>,
383        )
384        .await;
385        if let Ok(e) = &resp {
386            println!("{:#?}", e);
387        }
388        if let Err(e) = resp {
389            assert_eq!(e.error_no, ErrNo::DECODE)
390        }
391    }
392}