Skip to main content

sa_token_plugin_salvo_v079/
adapter.rs

1// Author: 金书记
2//
3// 中文 | English
4// Salvo 请求/响应适配器 | Salvo request/response adapter
5
6use salvo::prelude::*;
7use sa_token_adapter::{SaRequest, SaResponse, CookieOptions, build_cookie_string};
8use serde::Serialize;
9
10/// 中文 | English
11/// Salvo 请求适配器 | Salvo request adapter
12pub struct SalvoRequestAdapter<'a> {
13    request: &'a Request,
14}
15
16impl<'a> SalvoRequestAdapter<'a> {
17    /// 中文 | English
18    /// 创建新的请求适配器 | Create a new request adapter
19    pub fn new(request: &'a Request) -> Self {
20        Self { request }
21    }
22}
23
24impl<'a> SaRequest for SalvoRequestAdapter<'a> {
25    fn get_header(&self, name: &str) -> Option<String> {
26        self.request
27            .headers()
28            .get(name)
29            .and_then(|v| v.to_str().ok())
30            .map(|s| s.to_string())
31    }
32    
33    fn get_cookie(&self, name: &str) -> Option<String> {
34        // 先尝试使用 cookie 方法 | First try using cookie method
35        if let Some(cookie) = self.request.cookie(name) {
36            return Some(cookie.value().to_string());
37        }
38        
39        // 如果没有找到,手动解析 Cookie 头 | If not found, manually parse Cookie header
40        if let Some(cookie_header) = self.request.headers().get("cookie")
41            && let Ok(cookie_str) = cookie_header.to_str() {
42                let cookies = sa_token_adapter::utils::parse_cookies(cookie_str);
43                if let Some(value) = cookies.get(name) {
44                    return Some(value.to_string());
45                }
46            }
47        
48        None
49    }
50    
51    fn get_param(&self, name: &str) -> Option<String> {
52        // 先尝试使用 query 方法 | First try using query method
53        if let Some(value) = self.request.query::<String>(name) {
54            return Some(value);
55        }
56        
57        // 如果没有找到,手动解析查询字符串 | If not found, manually parse query string
58        if let Some(query) = self.request.uri().query() {
59            let params = sa_token_adapter::utils::parse_query_string(query);
60            if let Some(value) = params.get(name) {
61                return Some(value.to_string());
62            }
63        }
64        
65        None
66    }
67    
68    fn get_path(&self) -> String {
69        self.request.uri().path().to_string()
70    }
71    
72    fn get_method(&self) -> String {
73        self.request.method().to_string()
74    }
75
76    fn get_client_ip(&self) -> Option<String> {
77        Some(self.request.remote_addr().to_string())
78    }
79}
80
81/// Request-field snapshot before `await` for **`run_auth_flow`** / **`extract_token`**.
82/// `.await` 前的请求字段快照,供 **`run_auth_flow`** / **`extract_token`** 使用,避免借用 [`Request`] 跨过异步点。
83pub struct SalvoCapturedRequest {
84    token_name: String,
85    token_name_header: Option<String>,
86    authorization: Option<String>,
87    cookie_token: Option<String>,
88    query_token: Option<String>,
89    path: String,
90    method: String,
91    client_ip: Option<String>,
92}
93
94impl SalvoCapturedRequest {
95    /// Copy headers/cookies/query synchronously from live Salvo **`Request`**.
96    /// 从 Salvo **`Request`** 同步拷贝 Header/Cookie/Query。
97    pub fn capture(req: &Request, token_name: &str) -> Self {
98        let path = req.uri().path().to_string();
99        let method = req.method().to_string();
100        let client_ip = Some(req.remote_addr().to_string());
101        let token_name_header = req
102            .headers()
103            .get(token_name)
104            .and_then(|v| v.to_str().ok())
105            .map(|s| s.to_string());
106        let authorization = if !token_name.eq_ignore_ascii_case("authorization") {
107            req.headers()
108                .get("authorization")
109                .or_else(|| req.headers().get("Authorization"))
110                .and_then(|v| v.to_str().ok())
111                .map(|s| s.to_string())
112        } else {
113            None
114        };
115
116        let cookie_token = req.cookie(token_name).map(|c| c.value().to_string()).or_else(|| {
117            req.headers()
118                .get("cookie")
119                .and_then(|h| h.to_str().ok())
120                .and_then(|cookie_str| {
121                    let cookies = sa_token_adapter::utils::parse_cookies(cookie_str);
122                    cookies.get(token_name).map(|s| (*s).to_string())
123                })
124        });
125
126        let query_token = req
127            .query::<String>(token_name)
128            .or_else(|| {
129                req.uri().query().and_then(|q| {
130                    let params = sa_token_adapter::utils::parse_query_string(q);
131                    params.get(token_name).cloned()
132                })
133            });
134
135        Self {
136            token_name: token_name.to_string(),
137            token_name_header,
138            authorization,
139            cookie_token,
140            query_token,
141            path,
142            method,
143            client_ip,
144        }
145    }
146}
147
148impl SaRequest for SalvoCapturedRequest {
149    fn get_header(&self, name: &str) -> Option<String> {
150        if name.eq_ignore_ascii_case(&self.token_name) {
151            return self.token_name_header.clone();
152        }
153        if !self.token_name.eq_ignore_ascii_case("authorization")
154            && name.eq_ignore_ascii_case("authorization")
155        {
156            return self.authorization.clone();
157        }
158        None
159    }
160
161    fn get_cookie(&self, name: &str) -> Option<String> {
162        if name.eq_ignore_ascii_case(&self.token_name) {
163            self.cookie_token.clone()
164        } else {
165            None
166        }
167    }
168
169    fn get_param(&self, name: &str) -> Option<String> {
170        if name.eq_ignore_ascii_case(&self.token_name) {
171            self.query_token.clone()
172        } else {
173            None
174        }
175    }
176
177    fn get_path(&self) -> String {
178        self.path.clone()
179    }
180
181    fn get_method(&self) -> String {
182        self.method.clone()
183    }
184
185    fn get_client_ip(&self) -> Option<String> {
186        self.client_ip.clone()
187    }
188}
189
190/// 中文 | English
191/// Salvo 响应适配器 | Salvo response adapter
192pub struct SalvoResponseAdapter<'a> {
193    response: &'a mut Response,
194}
195
196impl<'a> SalvoResponseAdapter<'a> {
197    /// 中文 | English
198    /// 创建新的响应适配器 | Create a new response adapter
199    pub fn new(response: &'a mut Response) -> Self {
200        Self { response }
201    }
202}
203
204impl<'a> SaResponse for SalvoResponseAdapter<'a> {
205    fn set_header(&mut self, name: &str, value: &str) {
206        if let Ok(header_name) = http::header::HeaderName::from_bytes(name.as_bytes())
207            && let Ok(header_value) = http::header::HeaderValue::from_str(value) {
208                self.response.headers_mut().insert(header_name, header_value);
209            }
210    }
211    
212    fn set_cookie(&mut self, name: &str, value: &str, options: CookieOptions) {
213        let cookie_string = build_cookie_string(name, value, options);
214        self.set_header("Set-Cookie", &cookie_string);
215    }
216    
217    fn set_status(&mut self, status: u16) {
218        if let Ok(status_code) = http::StatusCode::from_u16(status) {
219            self.response.status_code(status_code);
220        }
221    }
222    
223    fn set_json_body<U: Serialize>(&mut self, body: U) -> Result<(), serde_json::Error> {
224        match serde_json::to_string(&body) {
225            Ok(json) => {
226                self.response.render(Text::Json(json));
227                Ok(())
228            }
229            Err(e) => Err(e),
230        }
231    }
232}
233