Skip to content

Commit 82b58a5

Browse files
authored
Merge pull request sa-tokens#24 from RuntimeBroker/main
补充文档缺口:Proc Macros、Storage、Adapter、框架集成 + 238 集成测试
2 parents c336f29 + 8b49343 commit 82b58a5

21 files changed

Lines changed: 1916 additions & 2056 deletions

doc/.vitepress/config.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,16 @@ const enSidebar = [
44
{
55
text: '🚀 Quick Start',
66
items: [
7-
{ text: 'Project Introduction', link: '/guide/project-intro' },
8-
{ text: 'Home', link: '/' },
7+
{ text: 'Home', link: '/' },
98
{ text: 'Quick Start Guide', link: '/guide/quick-start' },
109
]
1110
},
1211
{
1312
text: '📖 Basics',
1413
items: [
1514
{ text: 'StpUtil API Reference', link: '/guide/stp-util' },
16-
{ text: 'Permission Matching', link: '/guide/permission-matching' },
15+
{ text: 'Proc Macros', link: '/guide/permission-matching' },
1716
{ text: 'Event Listeners', link: '/guide/event-listener' },
18-
{ text: 'Event Listener Quick Start', link: '/guide/event-listener-quickstart' },
1917
{ text: 'Path Auth Guide', link: '/guide/path-auth' },
2018
{ text: 'Token Styles', link: '/guide/token-styles' },
2119
]
@@ -36,6 +34,8 @@ const enSidebar = [
3634
{
3735
text: 'Reference',
3836
items: [
37+
{ text: 'Storage Backends', link: '/guide/storage' },
38+
{ text: 'Adapters', link: '/guide/adapter' },
3939
{ text: 'Error Reference', link: '/reference/error-reference' },
4040
]
4141
},
@@ -45,7 +45,6 @@ const zhSidebar = [
4545
{
4646
text: '🚀 快速入门',
4747
items: [
48-
{ text: '项目介绍', link: '/zh/guide/project-intro' },
4948
{ text: '首页', link: '/zh/' },
5049
{ text: '快速入门指南', link: '/zh/guide/quick-start' },
5150
]
@@ -54,9 +53,8 @@ const zhSidebar = [
5453
text: '📖 基础',
5554
items: [
5655
{ text: 'StpUtil API 参考', link: '/zh/guide/stp-util' },
57-
{ text: '权限匹配规则', link: '/zh/guide/permission-matching' },
56+
{ text: '过程宏', link: '/zh/guide/permission-matching' },
5857
{ text: '事件监听器', link: '/zh/guide/event-listener' },
59-
{ text: '事件监听器快速入门', link: '/zh/guide/event-listener-quickstart' },
6058
{ text: '路径鉴权指南', link: '/zh/guide/path-auth' },
6159
{ text: 'Token 风格', link: '/zh/guide/token-styles' },
6260
]
@@ -77,6 +75,8 @@ const zhSidebar = [
7775
{
7876
text: '参考',
7977
items: [
78+
{ text: '存储后端', link: '/zh/guide/storage' },
79+
{ text: '适配器', link: '/zh/guide/adapter' },
8080
{ text: '错误参考', link: '/zh/reference/error-reference' },
8181
]
8282
},

doc/guide/adapter.md

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
# Framework Adapters
2+
3+
[中文文档](/zh/guide/adapter) | English
4+
5+
sa-token-rust communicates with web frameworks through adapter traits defined in `sa-token-adapter`. This page documents these low-level interfaces — useful for implementing custom framework plugins or debugging token extraction.
6+
7+
## SaRequest
8+
9+
The `SaRequest` trait abstracts HTTP request data. Each framework plugin provides an implementation for its request type.
10+
11+
```rust
12+
pub trait SaRequest {
13+
// Required
14+
fn get_header(&self, name: &str) -> Option<String>;
15+
fn get_cookie(&self, name: &str) -> Option<String>;
16+
fn get_param(&self, name: &str) -> Option<String>;
17+
fn get_path(&self) -> String;
18+
fn get_method(&self) -> String;
19+
20+
// With defaults
21+
fn get_headers(&self) -> HashMap<String, String> { HashMap::new() }
22+
fn get_cookies(&self) -> HashMap<String, String> { HashMap::new() }
23+
fn get_params(&self) -> HashMap<String, String> { HashMap::new() }
24+
fn get_uri(&self) -> String { self.get_path() }
25+
fn get_body_json<T: DeserializeOwned>(&self) -> Option<T> { None }
26+
fn get_client_ip(&self) -> Option<String> { None }
27+
fn get_user_agent(&self) -> Option<String> { self.get_header("user-agent") }
28+
}
29+
```
30+
31+
### Method Table
32+
33+
| Method | Required | Description |
34+
|---|---|---|
35+
| `get_header` | Yes | Read a single HTTP header |
36+
| `get_cookie` | Yes | Read a single cookie by name |
37+
| `get_param` | Yes | Read a single query parameter |
38+
| `get_path` | Yes | Get the request URL path |
39+
| `get_method` | Yes | Get the HTTP method |
40+
| `get_headers` | No | Read all headers as a map |
41+
| `get_cookies` | No | Read all cookies as a map |
42+
| `get_params` | No | Read all query parameters as a map |
43+
| `get_uri` | No | Get full request URI (default: path) |
44+
| `get_body_json` | No | Parse JSON body if available |
45+
| `get_client_ip` | No | Get client IP address |
46+
| `get_user_agent` | No | Get User-Agent header |
47+
48+
---
49+
50+
## SaResponse
51+
52+
Abstraction for setting response data.
53+
54+
```rust
55+
pub trait SaResponse {
56+
// Required
57+
fn set_header(&mut self, name: &str, value: &str);
58+
fn set_cookie(&mut self, name: &str, value: &str, options: CookieOptions);
59+
fn set_status(&mut self, status: u16);
60+
fn set_json_body<T: Serialize>(&mut self, body: T) -> Result<(), serde_json::Error>;
61+
62+
// With default
63+
fn delete_cookie(&mut self, name: &str) {
64+
self.set_cookie(name, "", CookieOptions { max_age: Some(0), ..Default::default() });
65+
}
66+
}
67+
```
68+
69+
---
70+
71+
## CookieOptions
72+
73+
Configure cookie attributes for `set_cookie`.
74+
75+
```rust
76+
pub struct CookieOptions {
77+
pub domain: Option<String>, // Cookie domain
78+
pub path: Option<String>, // Cookie path
79+
pub max_age: Option<i64>, // Max age in seconds
80+
pub http_only: bool, // HttpOnly flag
81+
pub secure: bool, // Secure flag (HTTPS only)
82+
pub same_site: Option<SameSite>, // SameSite attribute
83+
}
84+
85+
pub enum SameSite {
86+
Strict,
87+
Lax,
88+
None,
89+
}
90+
```
91+
92+
**Example:**
93+
94+
```rust
95+
let opts = CookieOptions {
96+
path: Some("/".into()),
97+
max_age: Some(86400),
98+
http_only: true,
99+
secure: true,
100+
same_site: Some(SameSite::Lax),
101+
..Default::default()
102+
};
103+
```
104+
105+
---
106+
107+
## Utility Functions
108+
109+
The `sa_token_adapter::utils` module provides helpers used across all plugins.
110+
111+
### `parse_cookies`
112+
113+
```rust
114+
pub fn parse_cookies(cookie_header: &str) -> HashMap<String, String>
115+
```
116+
117+
Parses a raw `Cookie` header string (`"key1=value1; key2=value2"`) into a key-value map. Does **not** URL-decode values.
118+
119+
### `parse_query_string`
120+
121+
```rust
122+
pub fn parse_query_string(query: &str) -> HashMap<String, String>
123+
```
124+
125+
Parses a query string (`"key1=value1&key2=value2"`) into a key-value map. **Does** URL-decode both keys and values.
126+
127+
### `build_cookie_string`
128+
129+
```rust
130+
pub fn build_cookie_string(name: &str, value: &str, options: CookieOptions) -> String
131+
```
132+
133+
Builds a complete `Set-Cookie` header value from name, value, and options. Automatically includes `Domain`, `Path`, `Max-Age`, `HttpOnly`, `Secure`, and `SameSite` attributes as appropriate.
134+
135+
### `strip_bearer_prefix`
136+
137+
```rust
138+
pub fn strip_bearer_prefix(auth_header: &str) -> Option<String>
139+
```
140+
141+
Strict: strips `Bearer ` prefix only. Returns `None` if not present. Use when you need to distinguish between Bearer tokens and other authorization schemes.
142+
143+
### `extract_bearer_or_value`
144+
145+
```rust
146+
pub fn extract_bearer_or_value(s: &str) -> String
147+
```
148+
149+
Lenient: strips `Bearer ` if present, otherwise returns trimmed input. This is what framework plugins use for token extraction from Authorization headers.
150+
151+
### `strip_bearer_or_passthrough`
152+
153+
```rust
154+
pub fn strip_bearer_or_passthrough(s: &str) -> String
155+
```
156+
157+
Alias of `extract_bearer_or_value`. Prefer `extract_bearer_or_value` in new code.
158+
159+
---
160+
161+
## FrameworkAdapter
162+
163+
Minimal lifecycle trait for framework plugins.
164+
165+
```rust
166+
pub trait FrameworkAdapter: Send + Sync {
167+
fn name(&self) -> &str;
168+
async fn initialize(&self) -> Result<(), String>;
169+
async fn shutdown(&self) -> Result<(), String> { Ok(()) }
170+
}
171+
```
172+
173+
Not required for basic usage. Used when a framework plugin needs initialization logic (e.g., Redis connection pool setup).
174+
175+
---
176+
177+
## Writing a Custom Plugin
178+
179+
```rust
180+
use sa_token_adapter::context::{SaRequest, SaResponse, CookieOptions};
181+
use sa_token_adapter::utils::{extract_bearer_or_value, parse_cookies};
182+
183+
struct MyRequestAdapter {
184+
headers: HashMap<String, String>,
185+
cookies: HashMap<String, String>,
186+
path: String,
187+
method: String,
188+
}
189+
190+
impl SaRequest for MyRequestAdapter {
191+
fn get_header(&self, name: &str) -> Option<String> {
192+
self.headers.get(name).cloned()
193+
}
194+
195+
fn get_cookie(&self, name: &str) -> Option<String> {
196+
// Parse the Cookie header
197+
self.get_header("cookie")
198+
.map(|h| parse_cookies(&h))
199+
.and_then(|c| c.get(name).cloned())
200+
}
201+
202+
fn get_param(&self, name: &str) -> Option<String> {
203+
// Parse from self.path query string if present
204+
None
205+
}
206+
207+
fn get_path(&self) -> String { self.path.clone() }
208+
fn get_method(&self) -> String { self.method.clone() }
209+
}
210+
```
211+
212+
## Related
213+
214+
- [Storage Backends](/guide/storage)
215+
- [Framework Integration](/guide/framework-integration)
216+
- [StpUtil API](/guide/stp-util)

0 commit comments

Comments
 (0)