Skip to content

Commit 26f4552

Browse files
committed
Update doc
1 parent 08613c0 commit 26f4552

File tree

1 file changed

+149
-39
lines changed

1 file changed

+149
-39
lines changed

examples/URIMatcher/README.md

Lines changed: 149 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,15 @@ The `AsyncURIMatcher` class provides flexible and powerful URL routing mechanism
88

99
**Important**: When using plain strings (not `AsyncURIMatcher` objects), the library uses auto-detection (`URIMatchAuto`) which analyzes the URI pattern and applies appropriate matching rules. This is **not** simple exact matching - it combines exact and folder matching by default!
1010

11+
## What's Demonstrated
12+
13+
This example includes two Arduino sketches:
14+
15+
1. **URIMatcher.ino** - Interactive web-based demonstration with a user-friendly homepage
16+
2. **URIMatcherTest.ino** - Comprehensive test suite with automated shell script testing
17+
18+
Both sketches create a WiFi Access Point (`esp-captive`) for easy testing without network configuration.
19+
1120
## Auto-Detection Behavior
1221

1322
When you pass a plain string or `const char*` to `server.on()`, the `URIMatchAuto` flag is used, which:
@@ -19,72 +28,159 @@ When you pass a plain string or `const char*` to `server.on()`, the `URIMatchAut
1928
5. **Everything else**: Becomes **both** exact and folder match (`URIMatchPrefixFolder | URIMatchExact`)
2029

2130
This means traditional string-based routes like `server.on("/path", handler)` will match:
31+
2232
- `/path` (exact match)
33+
- `/path/` (folder with trailing slash)
2334
- `/path/anything` (folder match)
2435

2536
But will **NOT** match `/path-suffix` (prefix without folder separator).
2637

2738
## Features Demonstrated
2839

2940
### 1. **Auto-Detection (Traditional Behavior)**
30-
- When using plain strings, automatically detects the appropriate matching strategy
31-
- Default behavior: combines exact and folder matching
32-
- Special patterns: `*` suffix → prefix, `/*.ext` → extension, regex patterns
33-
- Use cases: Traditional ESP32 web server behavior with enhanced capabilities
34-
- Examples: `/path` matches both `/path` and `/path/sub`
3541

36-
### 2. **Exact Matching**
37-
- Matches only the exact URL specified
38-
- Use cases: API endpoints, specific pages
39-
- Examples: `/exact`, `/login`, `/dashboard`
42+
Demonstrates how traditional string-based routing automatically combines exact and folder matching.
43+
44+
**Examples in URIMatcher.ino:**
45+
46+
- `/auto` - Matches both `/auto` exactly AND `/auto/sub` as folder
47+
- `/wildcard*` - Auto-detects as prefix match (due to trailing `*`)
48+
- `/auto-images/*.png` - Auto-detects as extension match (due to `/*.ext` pattern)
49+
50+
**Examples in URIMatcherTest.ino:**
51+
52+
- `/exact` - Matches `/exact`, `/exact/`, and `/exact/sub`
53+
- `/api/users` - Matches exact path and subpaths under `/api/users/`
54+
- `/*.json` - Matches any `.json` file anywhere
55+
- `/*.css` - Matches any `.css` file anywhere
56+
57+
### 2. **Exact Matching (Factory Method)**
58+
59+
Using `AsyncURIMatcher::exact()` matches only the exact URL, **NOT** subpaths.
60+
61+
**Key difference from auto-detection:** `AsyncURIMatcher::exact("/path")` matches **only** `/path`, while `server.on("/path", ...)` matches both `/path` and `/path/sub`.
62+
63+
**Examples in URIMatcher.ino:**
64+
65+
- `AsyncURIMatcher::exact("/exact")` - Matches only `/exact`
66+
67+
**Examples in URIMatcherTest.ino:**
68+
69+
- `AsyncURIMatcher::exact("/factory/exact")` - Matches only `/factory/exact`
70+
- Does NOT match `/factory/exact/sub` (404 response)
4071

4172
### 3. **Prefix Matching**
42-
- Matches URLs that start with the specified pattern
43-
- Use cases: API versioning, module grouping
44-
- Examples: `/api*` matches `/api/users`, `/api/posts`, etc.
73+
74+
Using `AsyncURIMatcher::prefix()` matches URLs that start with the specified pattern.
75+
76+
**Examples in URIMatcher.ino:**
77+
78+
- `AsyncURIMatcher::prefix("/service")` - Matches `/service`, `/service-test`, `/service/status`
79+
80+
**Examples in URIMatcherTest.ino:**
81+
82+
- `AsyncURIMatcher::prefix("/factory/prefix")` - Matches `/factory/prefix`, `/factory/prefix-test`, `/factory/prefix/sub`
83+
- Traditional: `/api/*` - Matches `/api/data`, `/api/v1/posts`
84+
- Traditional: `/files/*` - Matches `/files/document.pdf`, `/files/images/photo.jpg`
4585

4686
### 4. **Folder/Directory Matching**
47-
- Matches URLs under a specific directory path
48-
- Use cases: Admin sections, organized content
49-
- Examples: `/admin/` matches `/admin/users`, `/admin/settings`
87+
88+
Using `AsyncURIMatcher::dir()` matches URLs under a directory (automatically adds trailing slash).
89+
90+
**Important:** Directory matching requires a trailing slash in the URL - it does NOT match the directory itself.
91+
92+
**Examples in URIMatcher.ino:**
93+
94+
- `AsyncURIMatcher::dir("/admin")` - Matches `/admin/users`, `/admin/settings`
95+
- Does NOT match `/admin` without trailing slash
96+
97+
**Examples in URIMatcherTest.ino:**
98+
99+
- `AsyncURIMatcher::dir("/factory/dir")` - Matches `/factory/dir/users`, `/factory/dir/sub/path`
100+
- Does NOT match `/factory/dir` itself (404 response)
50101

51102
### 5. **Extension Matching**
52-
- Matches files with specific extensions under a path
53-
- Use cases: Static file serving, file type handlers
54-
- Examples: `/images/*.jpg` matches any `.jpg` file under `/images/`
103+
104+
Using `AsyncURIMatcher::ext()` matches files with specific extensions.
105+
106+
**Examples in URIMatcher.ino:**
107+
108+
- `AsyncURIMatcher::ext("/images/*.jpg")` - Matches `/images/photo.jpg`, `/images/sub/pic.jpg`
109+
110+
**Examples in URIMatcherTest.ino:**
111+
112+
- `AsyncURIMatcher::ext("/factory/files/*.txt")` - Matches `/factory/files/doc.txt`, `/factory/files/sub/readme.txt`
113+
- Does NOT match `/factory/files/doc.pdf` (wrong extension)
55114

56115
### 6. **Case Insensitive Matching**
57-
- Matches URLs regardless of character case
58-
- Use cases: User-friendly URLs, legacy support
59-
- Examples: `/CaSe` matches `/case`, `/CASE`, etc.
116+
117+
Using `AsyncURIMatcher::CaseInsensitive` flag matches URLs regardless of character case.
118+
119+
**Examples in URIMatcher.ino:**
120+
121+
- `AsyncURIMatcher::exact("/case", AsyncURIMatcher::CaseInsensitive)` - Matches `/case`, `/CASE`, `/CaSe`
122+
123+
**Examples in URIMatcherTest.ino:**
124+
125+
- Case insensitive exact: `/case/exact`, `/CASE/EXACT`, `/Case/Exact` all work
126+
- Case insensitive prefix: `/case/prefix`, `/CASE/PREFIX-test`, `/Case/Prefix/sub` all work
127+
- Case insensitive directory: `/case/dir/users`, `/CASE/DIR/admin`, `/Case/Dir/settings` all work
128+
- Case insensitive extension: `/case/files/doc.pdf`, `/CASE/FILES/DOC.PDF`, `/Case/Files/Doc.Pdf` all work
60129

61130
### 7. **Regular Expression Matching**
62-
- Advanced pattern matching using regex (requires `ASYNCWEBSERVER_REGEX`)
63-
- Use cases: Complex URL patterns, parameter extraction
64-
- Examples: `/user/([0-9]+)` matches `/user/123` and captures the ID
131+
132+
Using `AsyncURIMatcher::regex()` for advanced pattern matching (requires `ASYNCWEBSERVER_REGEX`).
133+
134+
**Examples in URIMatcher.ino:**
135+
136+
```cpp
137+
#ifdef ASYNCWEBSERVER_REGEX
138+
AsyncURIMatcher::regex("^/user/([0-9]+)$") // Matches /user/123, captures ID
139+
#endif
140+
```
141+
142+
**Examples in URIMatcherTest.ino:**
143+
144+
- Traditional regex: `^/user/([0-9]+)$` - Matches `/user/123`, `/user/456`
145+
- Traditional regex: `^/blog/([0-9]{4})/([0-9]{2})/([0-9]{2})$` - Matches `/blog/2023/10/15`
146+
- Factory regex: `AsyncURIMatcher::regex("^/factory/user/([0-9]+)$")` - Matches `/factory/user/123`
147+
- Factory regex with multiple captures: `^/factory/blog/([0-9]{4})/([0-9]{2})/([0-9]{2})$`
148+
- Case insensitive regex: `AsyncURIMatcher::regex("^/factory/search/(.+)$", AsyncURIMatcher::CaseInsensitive)`
65149

66150
### 8. **Combined Flags**
67-
- Multiple matching strategies can be combined
68-
- Use cases: Flexible routing requirements
69-
- Examples: Case-insensitive prefix matching
151+
152+
Multiple matching strategies can be combined using the `|` operator.
153+
154+
**Examples in URIMatcher.ino:**
155+
156+
- `AsyncURIMatcher::prefix("/MixedCase", AsyncURIMatcher::CaseInsensitive)` - Prefix match that's case insensitive
157+
158+
### 9. **Special Matchers**
159+
160+
**Examples in URIMatcherTest.ino:**
161+
162+
- `AsyncURIMatcher::all()` - Matches all requests (used with POST method as catch-all)
70163

71164
## Usage Patterns
72165

73166
### Traditional String-based Routing (Auto-Detection)
167+
74168
```cpp
75169
// Auto-detection with exact + folder matching
76170
server.on("/api", handler); // Matches /api AND /api/anything
77171
server.on("/login", handler); // Matches /login AND /login/sub
78172

79-
// Auto-detection with prefix matching
173+
// Auto-detection with prefix matching
80174
server.on("/prefix*", handler); // Matches /prefix, /prefix-test, /prefix/sub
81175

82176
// Auto-detection with extension matching
83177
server.on("/images/*.jpg", handler); // Matches /images/pic.jpg, /images/sub/pic.jpg
84178
```
85179

86180
### Explicit AsyncURIMatcher Syntax
181+
87182
### Explicit AsyncURIMatcher Syntax
183+
88184
```cpp
89185
// Exact matching only
90186
server.on(AsyncURIMatcher("/path", URIMatchExact), handler);
@@ -97,6 +193,7 @@ server.on(AsyncURIMatcher("/api", URIMatchPrefix | URIMatchCaseInsensitive), han
97193
```
98194

99195
### Factory Functions
196+
100197
```cpp
101198
// More readable and expressive
102199
server.on(AsyncURIMatcher::exact("/login"), handler);
@@ -112,15 +209,15 @@ server.on(AsyncURIMatcher::regex("^/user/([0-9]+)$"), handler);
112209

113210
## Available Flags
114211

115-
| Flag | Description |
116-
|------|-------------|
117-
| `URIMatchAuto` | Auto-detect match type from pattern (default) |
118-
| `URIMatchExact` | Exact URL match |
119-
| `URIMatchPrefix` | Prefix match |
120-
| `URIMatchPrefixFolder` | Folder prefix match (requires trailing /) |
121-
| `URIMatchExtension` | File extension match pattern |
122-
| `URIMatchCaseInsensitive` | Case insensitive matching |
123-
| `URIMatchRegex` | Regular expression matching (requires ASYNCWEBSERVER_REGEX) |
212+
| Flag | Description |
213+
| ------------------------- | ----------------------------------------------------------- |
214+
| `URIMatchAuto` | Auto-detect match type from pattern (default) |
215+
| `URIMatchExact` | Exact URL match |
216+
| `URIMatchPrefix` | Prefix match |
217+
| `URIMatchPrefixFolder` | Folder prefix match (requires trailing /) |
218+
| `URIMatchExtension` | File extension match pattern |
219+
| `URIMatchCaseInsensitive` | Case insensitive matching |
220+
| `URIMatchRegex` | Regular expression matching (requires ASYNCWEBSERVER_REGEX) |
124221

125222
## Testing the Example
126223

@@ -133,41 +230,47 @@ server.on(AsyncURIMatcher::regex("^/user/([0-9]+)$"), handler);
133230
### Test URLs Available (All Clickable from Homepage)
134231

135232
**Auto-Detection Examples:**
233+
136234
- `http://192.168.4.1/auto` (exact + folder match)
137235
- `http://192.168.4.1/auto/sub` (folder match - same handler!)
138236
- `http://192.168.4.1/wildcard-test` (auto-detected prefix)
139237
- `http://192.168.4.1/auto-images/photo.png` (auto-detected extension)
140238

141239
**Factory Method Examples:**
240+
142241
- `http://192.168.4.1/exact` (AsyncURIMatcher::exact)
143242
- `http://192.168.4.1/service/status` (AsyncURIMatcher::prefix)
144243
- `http://192.168.4.1/admin/users` (AsyncURIMatcher::dir)
145244
- `http://192.168.4.1/images/photo.jpg` (AsyncURIMatcher::ext)
146245

147246
**Case Insensitive Examples:**
247+
148248
- `http://192.168.4.1/case` (lowercase)
149249
- `http://192.168.4.1/CASE` (uppercase)
150250
- `http://192.168.4.1/CaSe` (mixed case)
151251

152252
**Regex Examples (if ASYNCWEBSERVER_REGEX enabled):**
253+
153254
- `http://192.168.4.1/user/123` (captures numeric ID)
154255
- `http://192.168.4.1/user/456` (captures numeric ID)
155256

156257
**Combined Flags Examples:**
258+
157259
- `http://192.168.4.1/mixedcase-test` (prefix + case insensitive)
158260
- `http://192.168.4.1/MIXEDCASE/sub` (prefix + case insensitive)
159261

160262
### Console Output
161263

162264
Each handler provides detailed debugging information via Serial output:
265+
163266
```
164267
Auto-Detection Match (Traditional)
165268
Matched URL: /auto
166269
Uses auto-detection: exact + folder matching
167270
```
168271

169272
```
170-
Factory Exact Match
273+
Factory Exact Match
171274
Matched URL: /exact
172275
Uses AsyncURIMatcher::exact() factory function
173276
```
@@ -182,32 +285,37 @@ This regex matches /user/{number} pattern
182285
## Compilation Options
183286

184287
### Enable Regex Support
288+
185289
To enable regular expression matching, compile with:
290+
186291
```
187292
-D ASYNCWEBSERVER_REGEX
188293
```
189294

190295
In PlatformIO, add to `platformio.ini`:
296+
191297
```ini
192298
build_flags = -D ASYNCWEBSERVER_REGEX
193299
```
194300

195301
In Arduino IDE, add to your sketch:
302+
196303
```cpp
197304
#define ASYNCWEBSERVER_REGEX
198305
```
199306

200307
## Performance Considerations
201308

202309
1. **Exact matches** are fastest
203-
2. **Prefix matches** are very efficient
310+
2. **Prefix matches** are very efficient
204311
3. **Regex matches** are slower but most flexible
205312
4. **Case insensitive** matching adds minimal overhead
206313
5. **Auto-detection** adds slight parsing overhead at construction time
207314

208315
## Real-World Applications
209316

210317
### REST API Design
318+
211319
```cpp
212320
// API versioning
213321
server.on(AsyncURIMatcher::prefix("/api/v1"), handleAPIv1);
@@ -219,6 +327,7 @@ server.on(AsyncURIMatcher::regex("^/api/posts/([0-9]+)/comments$"), handlePostCo
219327
```
220328

221329
### File Serving
330+
222331
```cpp
223332
// Serve different file types
224333
server.on(AsyncURIMatcher::ext("/assets/*.css"), serveCSSFiles);
@@ -227,6 +336,7 @@ server.on(AsyncURIMatcher::ext("/images/*.jpg"), serveImageFiles);
227336
```
228337

229338
### Admin Interface
339+
230340
```cpp
231341
// Admin section with authentication
232342
server.on(AsyncURIMatcher::dir("/admin"), handleAdminPages);

0 commit comments

Comments
 (0)