open_lark/core/api_req.rs
1use std::collections::HashMap;
2
3use reqwest::Method;
4
5use crate::core::constants::AccessTokenType;
6
7/// API请求的核心数据结构 - 命令模式的体现
8///
9/// `ApiRequest` 是整个SDK架构的核心,采用命令模式(Command Pattern)设计。
10/// 它封装了发起一次飞书API调用所需的所有信息,充当服务层(Service)与传输层(Transport)之间的桥梁。
11///
12/// # 设计理念
13///
14/// - **解耦性**:服务层只负责构建请求,不关心HTTP细节
15/// - **统一性**:所有API调用都通过这个统一的结构体表示
16/// - **灵活性**:支持普通请求和multipart/form-data请求
17///
18/// # 使用流程
19///
20/// 1. 服务层方法创建并配置 `ApiRequest` 实例
21/// 2. 设置HTTP方法、路径、认证需求等基本信息
22/// 3. 根据请求类型填充 `body` 和/或 `file` 字段
23/// 4. 将配置好的请求传递给 `Transport::request` 进行处理
24///
25/// # 示例
26///
27/// ```rust,ignore
28/// // 普通JSON请求
29/// let mut api_req = ApiRequest {
30/// http_method: Method::POST,
31/// api_path: "/open-apis/drive/v1/files".to_string(),
32/// body: serde_json::to_vec(&request_data).unwrap(),
33/// ..Default::default()
34/// };
35///
36/// // 文件上传请求(multipart)
37/// let mut api_req = ApiRequest {
38/// http_method: Method::POST,
39/// api_path: "/open-apis/drive/v1/files/upload".to_string(),
40/// body: serde_json::to_vec(&metadata).unwrap(), // JSON元数据
41/// file: file_bytes, // 文件内容
42/// ..Default::default()
43/// };
44/// ```
45#[derive(Debug, Clone, Default)]
46pub struct ApiRequest {
47 /// HTTP请求方法(GET、POST、PUT、DELETE等)
48 ///
49 /// 由服务层根据具体API的要求设置。
50 /// 使用 `pub(crate)` 限制只能在crate内部修改,保证一致性。
51 pub(crate) http_method: Method,
52
53 /// API的相对路径
54 ///
55 /// 例如:`/open-apis/drive/v1/files/{file_id}`
56 ///
57 /// 路径中的动态参数(如 `{file_id}`)通常通过 `format!` 宏直接嵌入,
58 /// 而不是使用 `path_params` 字段。
59 pub api_path: String,
60
61 /// 请求体数据(序列化后的字节数组)
62 ///
63 /// # 在不同请求类型中的用途:
64 ///
65 /// - **普通请求**:包含完整的请求体,通常是JSON序列化后的数据
66 /// - **文件上传(multipart)**:仅包含JSON元数据部分,文件内容存储在 `file` 字段
67 /// - **无请求体的请求**:保持为空 `Vec`
68 ///
69 /// # 注意事项
70 ///
71 /// 服务层通常使用 `serde_json::to_vec()` 将请求结构体序列化到这个字段。
72 pub body: Vec<u8>,
73
74 /// URL查询参数
75 ///
76 /// 存储将被附加到URL末尾的查询参数。
77 /// 例如:`?page_size=10&page_token=xxx`
78 ///
79 /// # 示例
80 ///
81 /// ```rust,ignore
82 /// api_req.query_params.insert("page_size".to_string(), "10".to_string());
83 /// api_req.query_params.insert("page_token".to_string(), token);
84 /// ```
85 pub query_params: HashMap<String, String>,
86
87 /// URL路径参数(当前未使用)
88 ///
89 /// 原设计意图可能是用于路径模板替换,如将 `/files/{id}` 中的 `{id}` 替换。
90 /// 但当前实现中,路径参数都是通过 `format!` 宏直接嵌入到 `api_path` 中。
91 ///
92 /// # TODO
93 ///
94 /// 考虑移除此字段或实现路径模板功能以保持设计一致性。
95 pub path_params: HashMap<String, Vec<String>>,
96
97 /// 支持的访问令牌类型
98 ///
99 /// 指定此API端点接受哪些类型的访问令牌:
100 /// - `User`:用户访问令牌
101 /// - `Tenant`:租户访问令牌
102 /// - `App`:应用访问令牌
103 ///
104 /// Transport层会根据这个列表和当前配置选择合适的令牌类型。
105 /// 使用 `pub(crate)` 确保只能由服务层设置。
106 pub(crate) supported_access_token_types: Vec<AccessTokenType>,
107
108 /// 文件内容(用于multipart/form-data请求)
109 ///
110 /// # 在不同请求类型中的用途:
111 ///
112 /// - **普通请求**:保持为空 `Vec`
113 /// - **文件上传(multipart)**:包含要上传的文件的二进制内容
114 ///
115 /// # 工作原理
116 ///
117 /// 当 `file` 字段非空时,Transport层会自动识别这是一个multipart请求:
118 /// 1. `body` 字段的内容作为multipart的JSON元数据部分
119 /// 2. `file` 字段的内容作为文件部分
120 /// 3. Content-Type自动设置为 `multipart/form-data`
121 ///
122 /// # 示例
123 ///
124 /// ```rust,ignore
125 /// // 文件上传请求
126 /// api_req.body = serde_json::to_vec(&FileMetadata {
127 /// name: "document.pdf",
128 /// parent_id: "folder123",
129 /// }).unwrap();
130 /// api_req.file = std::fs::read("path/to/document.pdf").unwrap();
131 /// ```
132 pub file: Vec<u8>,
133}