Update to version 3.9.6
This commit is contained in:
349
examples/URIMatcher/README.md
Normal file
349
examples/URIMatcher/README.md
Normal file
@@ -0,0 +1,349 @@
|
||||
# AsyncURIMatcher Example
|
||||
|
||||
This example demonstrates the comprehensive URI matching capabilities of the ESPAsyncWebServer library using the `AsyncURIMatcher` class.
|
||||
|
||||
## Overview
|
||||
|
||||
The `AsyncURIMatcher` class provides flexible and powerful URL routing mechanisms that go beyond simple string matching. It supports various matching strategies that can be combined to create sophisticated routing rules.
|
||||
|
||||
**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!
|
||||
|
||||
## What's Demonstrated
|
||||
|
||||
This example includes two Arduino sketches:
|
||||
|
||||
1. **URIMatcher.ino** - Interactive web-based demonstration with a user-friendly homepage
|
||||
2. **URIMatcherTest.ino** - Comprehensive test suite with automated shell script testing
|
||||
|
||||
Both sketches create a WiFi Access Point (`esp-captive`) for easy testing without network configuration.
|
||||
|
||||
## Auto-Detection Behavior
|
||||
|
||||
When you pass a plain string or `const char*` to `server.on()`, the `URIMatchAuto` flag is used, which:
|
||||
|
||||
1. **Empty URI**: Matches everything
|
||||
2. **Ends with `*`**: Becomes prefix match (`URIMatchPrefix`)
|
||||
3. **Contains `/*.ext`**: Becomes extension match (`URIMatchExtension`)
|
||||
4. **Starts with `^` and ends with `$`**: Becomes regex match (if enabled)
|
||||
5. **Everything else**: Becomes **both** exact and folder match (`URIMatchPrefixFolder | URIMatchExact`)
|
||||
|
||||
This means traditional string-based routes like `server.on("/path", handler)` will match:
|
||||
|
||||
- `/path` (exact match)
|
||||
- `/path/` (folder with trailing slash)
|
||||
- `/path/anything` (folder match)
|
||||
|
||||
But will **NOT** match `/path-suffix` (prefix without folder separator).
|
||||
|
||||
## Features Demonstrated
|
||||
|
||||
### 1. **Auto-Detection (Traditional Behavior)**
|
||||
|
||||
Demonstrates how traditional string-based routing automatically combines exact and folder matching.
|
||||
|
||||
**Examples in URIMatcher.ino:**
|
||||
|
||||
- `/auto` - Matches both `/auto` exactly AND `/auto/sub` as folder
|
||||
- `/wildcard*` - Auto-detects as prefix match (due to trailing `*`)
|
||||
- `/auto-images/*.png` - Auto-detects as extension match (due to `/*.ext` pattern)
|
||||
|
||||
**Examples in URIMatcherTest.ino:**
|
||||
|
||||
- `/exact` - Matches `/exact`, `/exact/`, and `/exact/sub`
|
||||
- `/api/users` - Matches exact path and subpaths under `/api/users/`
|
||||
- `/*.json` - Matches any `.json` file anywhere
|
||||
- `/*.css` - Matches any `.css` file anywhere
|
||||
|
||||
### 2. **Exact Matching (Factory Method)**
|
||||
|
||||
Using `AsyncURIMatcher::exact()` matches only the exact URL, **NOT** subpaths.
|
||||
|
||||
**Key difference from auto-detection:** `AsyncURIMatcher::exact("/path")` matches **only** `/path`, while `server.on("/path", ...)` matches both `/path` and `/path/sub`.
|
||||
|
||||
**Examples in URIMatcher.ino:**
|
||||
|
||||
- `AsyncURIMatcher::exact("/exact")` - Matches only `/exact`
|
||||
|
||||
**Examples in URIMatcherTest.ino:**
|
||||
|
||||
- `AsyncURIMatcher::exact("/factory/exact")` - Matches only `/factory/exact`
|
||||
- Does NOT match `/factory/exact/sub` (404 response)
|
||||
|
||||
### 3. **Prefix Matching**
|
||||
|
||||
Using `AsyncURIMatcher::prefix()` matches URLs that start with the specified pattern.
|
||||
|
||||
**Examples in URIMatcher.ino:**
|
||||
|
||||
- `AsyncURIMatcher::prefix("/service")` - Matches `/service`, `/service-test`, `/service/status`
|
||||
|
||||
**Examples in URIMatcherTest.ino:**
|
||||
|
||||
- `AsyncURIMatcher::prefix("/factory/prefix")` - Matches `/factory/prefix`, `/factory/prefix-test`, `/factory/prefix/sub`
|
||||
- Traditional: `/api/*` - Matches `/api/data`, `/api/v1/posts`
|
||||
- Traditional: `/files/*` - Matches `/files/document.pdf`, `/files/images/photo.jpg`
|
||||
|
||||
### 4. **Folder/Directory Matching**
|
||||
|
||||
Using `AsyncURIMatcher::dir()` matches URLs under a directory (automatically adds trailing slash).
|
||||
|
||||
**Important:** Directory matching requires a trailing slash in the URL - it does NOT match the directory itself.
|
||||
|
||||
**Examples in URIMatcher.ino:**
|
||||
|
||||
- `AsyncURIMatcher::dir("/admin")` - Matches `/admin/users`, `/admin/settings`
|
||||
- Does NOT match `/admin` without trailing slash
|
||||
|
||||
**Examples in URIMatcherTest.ino:**
|
||||
|
||||
- `AsyncURIMatcher::dir("/factory/dir")` - Matches `/factory/dir/users`, `/factory/dir/sub/path`
|
||||
- Does NOT match `/factory/dir` itself (404 response)
|
||||
|
||||
### 5. **Extension Matching**
|
||||
|
||||
Using `AsyncURIMatcher::ext()` matches files with specific extensions.
|
||||
|
||||
**Examples in URIMatcher.ino:**
|
||||
|
||||
- `AsyncURIMatcher::ext("/images/*.jpg")` - Matches `/images/photo.jpg`, `/images/sub/pic.jpg`
|
||||
|
||||
**Examples in URIMatcherTest.ino:**
|
||||
|
||||
- `AsyncURIMatcher::ext("/factory/files/*.txt")` - Matches `/factory/files/doc.txt`, `/factory/files/sub/readme.txt`
|
||||
- Does NOT match `/factory/files/doc.pdf` (wrong extension)
|
||||
|
||||
### 6. **Case Insensitive Matching**
|
||||
|
||||
Using `AsyncURIMatcher::CaseInsensitive` flag matches URLs regardless of character case.
|
||||
|
||||
**Examples in URIMatcher.ino:**
|
||||
|
||||
- `AsyncURIMatcher::exact("/case", AsyncURIMatcher::CaseInsensitive)` - Matches `/case`, `/CASE`, `/CaSe`
|
||||
|
||||
**Examples in URIMatcherTest.ino:**
|
||||
|
||||
- Case insensitive exact: `/case/exact`, `/CASE/EXACT`, `/Case/Exact` all work
|
||||
- Case insensitive prefix: `/case/prefix`, `/CASE/PREFIX-test`, `/Case/Prefix/sub` all work
|
||||
- Case insensitive directory: `/case/dir/users`, `/CASE/DIR/admin`, `/Case/Dir/settings` all work
|
||||
- Case insensitive extension: `/case/files/doc.pdf`, `/CASE/FILES/DOC.PDF`, `/Case/Files/Doc.Pdf` all work
|
||||
|
||||
### 7. **Regular Expression Matching**
|
||||
|
||||
Using `AsyncURIMatcher::regex()` for advanced pattern matching (requires `ASYNCWEBSERVER_REGEX`).
|
||||
|
||||
**Examples in URIMatcher.ino:**
|
||||
|
||||
```cpp
|
||||
#ifdef ASYNCWEBSERVER_REGEX
|
||||
AsyncURIMatcher::regex("^/user/([0-9]+)$") // Matches /user/123, captures ID
|
||||
#endif
|
||||
```
|
||||
|
||||
**Examples in URIMatcherTest.ino:**
|
||||
|
||||
- Traditional regex: `^/user/([0-9]+)$` - Matches `/user/123`, `/user/456`
|
||||
- Traditional regex: `^/blog/([0-9]{4})/([0-9]{2})/([0-9]{2})$` - Matches `/blog/2023/10/15`
|
||||
- Factory regex: `AsyncURIMatcher::regex("^/factory/user/([0-9]+)$")` - Matches `/factory/user/123`
|
||||
- Factory regex with multiple captures: `^/factory/blog/([0-9]{4})/([0-9]{2})/([0-9]{2})$`
|
||||
- Case insensitive regex: `AsyncURIMatcher::regex("^/factory/search/(.+)$", AsyncURIMatcher::CaseInsensitive)`
|
||||
|
||||
### 8. **Combined Flags**
|
||||
|
||||
Multiple matching strategies can be combined using the `|` operator.
|
||||
|
||||
**Examples in URIMatcher.ino:**
|
||||
|
||||
- `AsyncURIMatcher::prefix("/MixedCase", AsyncURIMatcher::CaseInsensitive)` - Prefix match that's case insensitive
|
||||
|
||||
### 9. **Special Matchers**
|
||||
|
||||
**Examples in URIMatcherTest.ino:**
|
||||
|
||||
- `AsyncURIMatcher::all()` - Matches all requests (used with POST method as catch-all)
|
||||
|
||||
## Usage Patterns
|
||||
|
||||
### Traditional String-based Routing (Auto-Detection)
|
||||
|
||||
```cpp
|
||||
// Auto-detection with exact + folder matching
|
||||
server.on("/api", handler); // Matches /api AND /api/anything
|
||||
server.on("/login", handler); // Matches /login AND /login/sub
|
||||
|
||||
// Auto-detection with prefix matching
|
||||
server.on("/prefix*", handler); // Matches /prefix, /prefix-test, /prefix/sub
|
||||
|
||||
// Auto-detection with extension matching
|
||||
server.on("/images/*.jpg", handler); // Matches /images/pic.jpg, /images/sub/pic.jpg
|
||||
```
|
||||
|
||||
### Explicit AsyncURIMatcher Syntax
|
||||
|
||||
### Explicit AsyncURIMatcher Syntax
|
||||
|
||||
```cpp
|
||||
// Exact matching only
|
||||
server.on(AsyncURIMatcher("/path", URIMatchExact), handler);
|
||||
|
||||
// Prefix matching only
|
||||
server.on(AsyncURIMatcher("/api", URIMatchPrefix), handler);
|
||||
|
||||
// Combined flags
|
||||
server.on(AsyncURIMatcher("/api", URIMatchPrefix | URIMatchCaseInsensitive), handler);
|
||||
```
|
||||
|
||||
### Factory Functions
|
||||
|
||||
```cpp
|
||||
// More readable and expressive
|
||||
server.on(AsyncURIMatcher::exact("/login"), handler);
|
||||
server.on(AsyncURIMatcher::prefix("/api"), handler);
|
||||
server.on(AsyncURIMatcher::dir("/admin"), handler);
|
||||
server.on(AsyncURIMatcher::ext("/images/*.jpg"), handler);
|
||||
|
||||
#ifdef ASYNCWEBSERVER_REGEX
|
||||
server.on(AsyncURIMatcher::regex("^/user/([0-9]+)$"), handler);
|
||||
#endif
|
||||
```
|
||||
|
||||
## Available Flags
|
||||
|
||||
| Flag | Description |
|
||||
| ------------------------- | ----------------------------------------------------------- |
|
||||
| `URIMatchAuto` | Auto-detect match type from pattern (default) |
|
||||
| `URIMatchExact` | Exact URL match |
|
||||
| `URIMatchPrefix` | Prefix match |
|
||||
| `URIMatchPrefixFolder` | Folder prefix match (requires trailing /) |
|
||||
| `URIMatchExtension` | File extension match pattern |
|
||||
| `URIMatchCaseInsensitive` | Case insensitive matching |
|
||||
| `URIMatchRegex` | Regular expression matching (requires ASYNCWEBSERVER_REGEX) |
|
||||
|
||||
## Testing the Example
|
||||
|
||||
1. **Upload the sketch** to your ESP32/ESP8266
|
||||
2. **Connect to WiFi AP**: `esp-captive` (no password required)
|
||||
3. **Navigate to**: `http://192.168.4.1/`
|
||||
4. **Explore the examples** by clicking the organized test links
|
||||
5. **Monitor Serial output**: Open Serial Monitor to see detailed debugging information for each matched route
|
||||
|
||||
### Test URLs Available (All Clickable from Homepage)
|
||||
|
||||
**Auto-Detection Examples:**
|
||||
|
||||
- `http://192.168.4.1/auto` (exact + folder match)
|
||||
- `http://192.168.4.1/auto/sub` (folder match - same handler!)
|
||||
- `http://192.168.4.1/wildcard-test` (auto-detected prefix)
|
||||
- `http://192.168.4.1/auto-images/photo.png` (auto-detected extension)
|
||||
|
||||
**Factory Method Examples:**
|
||||
|
||||
- `http://192.168.4.1/exact` (AsyncURIMatcher::exact)
|
||||
- `http://192.168.4.1/service/status` (AsyncURIMatcher::prefix)
|
||||
- `http://192.168.4.1/admin/users` (AsyncURIMatcher::dir)
|
||||
- `http://192.168.4.1/images/photo.jpg` (AsyncURIMatcher::ext)
|
||||
|
||||
**Case Insensitive Examples:**
|
||||
|
||||
- `http://192.168.4.1/case` (lowercase)
|
||||
- `http://192.168.4.1/CASE` (uppercase)
|
||||
- `http://192.168.4.1/CaSe` (mixed case)
|
||||
|
||||
**Regex Examples (if ASYNCWEBSERVER_REGEX enabled):**
|
||||
|
||||
- `http://192.168.4.1/user/123` (captures numeric ID)
|
||||
- `http://192.168.4.1/user/456` (captures numeric ID)
|
||||
|
||||
**Combined Flags Examples:**
|
||||
|
||||
- `http://192.168.4.1/mixedcase-test` (prefix + case insensitive)
|
||||
- `http://192.168.4.1/MIXEDCASE/sub` (prefix + case insensitive)
|
||||
|
||||
### Console Output
|
||||
|
||||
Each handler provides detailed debugging information via Serial output:
|
||||
|
||||
```
|
||||
Auto-Detection Match (Traditional)
|
||||
Matched URL: /auto
|
||||
Uses auto-detection: exact + folder matching
|
||||
```
|
||||
|
||||
```
|
||||
Factory Exact Match
|
||||
Matched URL: /exact
|
||||
Uses AsyncURIMatcher::exact() factory function
|
||||
```
|
||||
|
||||
```
|
||||
Regex Match - User ID
|
||||
Matched URL: /user/123
|
||||
Captured User ID: 123
|
||||
This regex matches /user/{number} pattern
|
||||
```
|
||||
|
||||
## Compilation Options
|
||||
|
||||
### Enable Regex Support
|
||||
|
||||
To enable regular expression matching, compile with:
|
||||
|
||||
```
|
||||
-D ASYNCWEBSERVER_REGEX
|
||||
```
|
||||
|
||||
In PlatformIO, add to `platformio.ini`:
|
||||
|
||||
```ini
|
||||
build_flags = -D ASYNCWEBSERVER_REGEX
|
||||
```
|
||||
|
||||
In Arduino IDE, add to your sketch:
|
||||
|
||||
```cpp
|
||||
#define ASYNCWEBSERVER_REGEX
|
||||
```
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
1. **Exact matches** are fastest
|
||||
2. **Prefix matches** are very efficient
|
||||
3. **Regex matches** are slower but most flexible
|
||||
4. **Case insensitive** matching adds minimal overhead
|
||||
5. **Auto-detection** adds slight parsing overhead at construction time
|
||||
|
||||
## Real-World Applications
|
||||
|
||||
### REST API Design
|
||||
|
||||
```cpp
|
||||
// API versioning
|
||||
server.on(AsyncURIMatcher::prefix("/api/v1"), handleAPIv1);
|
||||
server.on(AsyncURIMatcher::prefix("/api/v2"), handleAPIv2);
|
||||
|
||||
// Resource endpoints with IDs
|
||||
server.on(AsyncURIMatcher::regex("^/api/users/([0-9]+)$"), handleUserById);
|
||||
server.on(AsyncURIMatcher::regex("^/api/posts/([0-9]+)/comments$"), handlePostComments);
|
||||
```
|
||||
|
||||
### File Serving
|
||||
|
||||
```cpp
|
||||
// Serve different file types
|
||||
server.on(AsyncURIMatcher::ext("/assets/*.css"), serveCSSFiles);
|
||||
server.on(AsyncURIMatcher::ext("/assets/*.js"), serveJSFiles);
|
||||
server.on(AsyncURIMatcher::ext("/images/*.jpg"), serveImageFiles);
|
||||
```
|
||||
|
||||
### Admin Interface
|
||||
|
||||
```cpp
|
||||
// Admin section with authentication
|
||||
server.on(AsyncURIMatcher::dir("/admin"), handleAdminPages);
|
||||
server.on(AsyncURIMatcher::exact("/admin"), redirectToAdminDashboard);
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [ESPAsyncWebServer Documentation](https://github.com/ESP32Async/ESPAsyncWebServer)
|
||||
- [Regular Expression Reference](https://en.cppreference.com/w/cpp/regex)
|
||||
- Other examples in the `examples/` directory
|
||||
276
examples/URIMatcher/URIMatcher.ino
Normal file
276
examples/URIMatcher/URIMatcher.ino
Normal file
@@ -0,0 +1,276 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2026 Hristo Gochkov, Mathieu Carbou, Emil Muratov, Will Miles
|
||||
|
||||
//
|
||||
// AsyncURIMatcher Examples - Advanced URI Matching and Routing
|
||||
//
|
||||
// This example demonstrates the various ways to use AsyncURIMatcher class
|
||||
// for flexible URL routing with different matching strategies:
|
||||
//
|
||||
// 1. Exact matching
|
||||
// 2. Prefix matching
|
||||
// 3. Folder/directory matching
|
||||
// 4. Extension matching
|
||||
// 5. Case insensitive matching
|
||||
// 6. Regex matching (if ASYNCWEBSERVER_REGEX is enabled)
|
||||
// 7. Factory functions for common patterns
|
||||
//
|
||||
// Test URLs:
|
||||
// - Exact: http://192.168.4.1/exact
|
||||
// - Prefix: http://192.168.4.1/prefix-anything
|
||||
// - Folder: http://192.168.4.1/api/users, http://192.168.4.1/api/posts
|
||||
// - Extension: http://192.168.4.1/images/photo.jpg, http://192.168.4.1/docs/readme.pdf
|
||||
// - Case insensitive: http://192.168.4.1/CaSe or http://192.168.4.1/case
|
||||
// - Wildcard: http://192.168.4.1/wildcard-test
|
||||
|
||||
#include <Arduino.h>
|
||||
#if defined(ESP32) || defined(LIBRETINY)
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <RPAsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
static AsyncWebServer server(80);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println();
|
||||
Serial.println("=== AsyncURIMatcher Example ===");
|
||||
|
||||
#if ASYNCWEBSERVER_WIFI_SUPPORTED
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP("esp-captive");
|
||||
Serial.print("AP IP address: ");
|
||||
Serial.println(WiFi.softAPIP());
|
||||
#endif
|
||||
|
||||
// =============================================================================
|
||||
// 1. AUTO-DETECTION BEHAVIOR - traditional string-based routing
|
||||
// =============================================================================
|
||||
|
||||
// Traditional string-based routing with auto-detection
|
||||
// This uses URIMatchAuto which combines URIMatchPrefixFolder | URIMatchExact
|
||||
// It will match BOTH "/auto" exactly AND "/auto/" + anything
|
||||
server.on("/auto", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
Serial.println("Auto-Detection Match (Traditional)");
|
||||
Serial.println("Matched URL: " + request->url());
|
||||
Serial.println("Uses auto-detection: exact + folder matching");
|
||||
request->send(200, "text/plain", "OK - Auto-detection match");
|
||||
});
|
||||
|
||||
// Auto-detection for wildcard patterns (ends with *)
|
||||
// This auto-detects as URIMatchPrefix
|
||||
server.on("/wildcard*", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
Serial.println("Auto-Detected Wildcard (Prefix)");
|
||||
Serial.println("Matched URL: " + request->url());
|
||||
Serial.println("Auto-detected as prefix match due to trailing *");
|
||||
request->send(200, "text/plain", "OK - Wildcard prefix match");
|
||||
});
|
||||
|
||||
// Auto-detection for extension patterns (contains /*.ext)
|
||||
// This auto-detects as URIMatchExtension
|
||||
server.on("/auto-images/*.png", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
Serial.println("Auto-Detected Extension Pattern");
|
||||
Serial.println("Matched URL: " + request->url());
|
||||
Serial.println("Auto-detected as extension match due to /*.png pattern");
|
||||
request->send(200, "text/plain", "OK - Extension match");
|
||||
});
|
||||
|
||||
// =============================================================================
|
||||
// 2. EXACT MATCHING - matches only the exact URL (explicit)
|
||||
// =============================================================================
|
||||
|
||||
// Using factory function for exact match
|
||||
server.on(AsyncURIMatcher::exact("/exact"), HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
Serial.println("Factory Exact Match");
|
||||
Serial.println("Matched URL: " + request->url());
|
||||
Serial.println("Uses AsyncURIMatcher::exact() factory function");
|
||||
request->send(200, "text/plain", "OK - Factory exact match");
|
||||
});
|
||||
|
||||
// =============================================================================
|
||||
// 3. PREFIX MATCHING - matches URLs that start with the pattern
|
||||
// =============================================================================
|
||||
|
||||
// Using factory function for prefix match
|
||||
server.on(AsyncURIMatcher::prefix("/service"), HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
Serial.println("Service Prefix Match (Factory)");
|
||||
Serial.println("Matched URL: " + request->url());
|
||||
Serial.println("Uses AsyncURIMatcher::prefix() factory function");
|
||||
request->send(200, "text/plain", "OK - Factory prefix match");
|
||||
});
|
||||
|
||||
// =============================================================================
|
||||
// 4. FOLDER/DIRECTORY MATCHING - matches URLs in a folder structure
|
||||
// =============================================================================
|
||||
|
||||
// Folder match using factory function (automatically adds trailing slash)
|
||||
server.on(AsyncURIMatcher::dir("/admin"), HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
Serial.println("Admin Directory Match");
|
||||
Serial.println("Matched URL: " + request->url());
|
||||
Serial.println("This matches URLs under /admin/ directory");
|
||||
Serial.println("Note: /admin (without slash) will NOT match");
|
||||
request->send(200, "text/plain", "OK - Directory match");
|
||||
});
|
||||
|
||||
// =============================================================================
|
||||
// 5. EXTENSION MATCHING - matches files with specific extensions
|
||||
// =============================================================================
|
||||
|
||||
// Image extension matching
|
||||
server.on(AsyncURIMatcher::ext("/images/*.jpg"), HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
Serial.println("JPG Image Handler");
|
||||
Serial.println("Matched URL: " + request->url());
|
||||
Serial.println("This matches any .jpg file under /images/");
|
||||
request->send(200, "text/plain", "OK - Extension match");
|
||||
});
|
||||
|
||||
// =============================================================================
|
||||
// 6. CASE INSENSITIVE MATCHING
|
||||
// =============================================================================
|
||||
|
||||
// Case insensitive exact match
|
||||
server.on(AsyncURIMatcher::exact("/case", AsyncURIMatcher::CaseInsensitive), HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
Serial.println("Case Insensitive Match");
|
||||
Serial.println("Matched URL: " + request->url());
|
||||
Serial.println("This matches /case in any case combination");
|
||||
request->send(200, "text/plain", "OK - Case insensitive match");
|
||||
});
|
||||
|
||||
#ifdef ASYNCWEBSERVER_REGEX
|
||||
// =============================================================================
|
||||
// 7. REGEX MATCHING (only available if ASYNCWEBSERVER_REGEX is enabled)
|
||||
// =============================================================================
|
||||
|
||||
// Regex match for numeric IDs
|
||||
server.on(AsyncURIMatcher::regex("^/user/([0-9]+)$"), HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
Serial.println("Regex Match - User ID");
|
||||
Serial.println("Matched URL: " + request->url());
|
||||
if (request->pathArg(0).length() > 0) {
|
||||
Serial.println("Captured User ID: " + request->pathArg(0));
|
||||
}
|
||||
Serial.println("This regex matches /user/{number} pattern");
|
||||
request->send(200, "text/plain", "OK - Regex match");
|
||||
});
|
||||
#endif
|
||||
|
||||
// =============================================================================
|
||||
// 8. COMBINED FLAGS EXAMPLE
|
||||
// =============================================================================
|
||||
|
||||
// Combine multiple flags
|
||||
server.on(AsyncURIMatcher::prefix("/MixedCase", AsyncURIMatcher::CaseInsensitive), HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
Serial.println("Combined Flags Example");
|
||||
Serial.println("Matched URL: " + request->url());
|
||||
Serial.println("Uses both AsyncURIMatcher::Prefix and AsyncURIMatcher::CaseInsensitive");
|
||||
request->send(200, "text/plain", "OK - Combined flags match");
|
||||
});
|
||||
|
||||
// =============================================================================
|
||||
// 9. HOMEPAGE WITH NAVIGATION
|
||||
// =============================================================================
|
||||
|
||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
Serial.println("Homepage accessed");
|
||||
String response = R"(<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>AsyncURIMatcher Examples</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; margin: 20px; }
|
||||
h1 { color: #2E86AB; }
|
||||
.test-link { display: block; margin: 5px 0; padding: 8px; background: #f0f0f0; text-decoration: none; color: #333; border-radius: 4px; }
|
||||
.test-link:hover { background: #e0e0e0; }
|
||||
.section { margin: 20px 0; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>AsyncURIMatcher Examples</h1>
|
||||
|
||||
<div class="section">
|
||||
<h3>Auto-Detection (Traditional String Matching)</h3>
|
||||
<a href="/auto" class="test-link">/auto (auto-detection: exact + folder)</a>
|
||||
<a href="/auto/sub" class="test-link">/auto/sub (folder match)</a>
|
||||
<a href="/wildcard-test" class="test-link">/wildcard-test (auto prefix)</a>
|
||||
<a href="/auto-images/photo.png" class="test-link">/auto-images/photo.png (auto extension)</a>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h3>Factory Method Examples</h3>
|
||||
<a href="/exact" class="test-link">/exact (factory exact)</a>
|
||||
<a href="/service/status" class="test-link">/service/status (factory prefix)</a>
|
||||
<a href="/admin/users" class="test-link">/admin/users (factory directory)</a>
|
||||
<a href="/images/photo.jpg" class="test-link">/images/photo.jpg (factory extension)</a>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h3>Case Insensitive Matching</h3>
|
||||
<a href="/case" class="test-link">/case (lowercase)</a>
|
||||
<a href="/CASE" class="test-link">/CASE (uppercase)</a>
|
||||
<a href="/CaSe" class="test-link">/CaSe (mixed case)</a>
|
||||
</div>
|
||||
|
||||
)";
|
||||
#ifdef ASYNCWEBSERVER_REGEX
|
||||
response += R"( <div class="section">
|
||||
<h3>Regex Matching</h3>
|
||||
<a href="/user/123" class="test-link">/user/123 (regex numeric ID)</a>
|
||||
<a href="/user/456" class="test-link">/user/456 (regex numeric ID)</a>
|
||||
</div>
|
||||
|
||||
)";
|
||||
#endif
|
||||
response += R"( <div class="section">
|
||||
<h3>Combined Flags</h3>
|
||||
<a href="/mixedcase-test" class="test-link">/mixedcase-test (prefix + case insensitive)</a>
|
||||
<a href="/MIXEDCASE/sub" class="test-link">/MIXEDCASE/sub (prefix + case insensitive)</a>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>)";
|
||||
request->send(200, "text/html", response);
|
||||
});
|
||||
|
||||
// =============================================================================
|
||||
// 10. NOT FOUND HANDLER
|
||||
// =============================================================================
|
||||
|
||||
server.onNotFound([](AsyncWebServerRequest *request) {
|
||||
String html = "<h1>404 - Not Found</h1>";
|
||||
html += "<p>The requested URL <strong>" + request->url() + "</strong> was not found.</p>";
|
||||
html += "<p><a href='/'>← Back to Examples</a></p>";
|
||||
request->send(404, "text/html", html);
|
||||
});
|
||||
|
||||
server.begin();
|
||||
|
||||
Serial.println();
|
||||
Serial.println("=== Server Started ===");
|
||||
Serial.println("Open your browser and navigate to:");
|
||||
Serial.println("http://192.168.4.1/ - Main examples page");
|
||||
Serial.println();
|
||||
Serial.println("Available test endpoints:");
|
||||
Serial.println("• Auto-detection: /auto (exact+folder), /wildcard*, /auto-images/*.png");
|
||||
Serial.println("• Exact matches: /exact");
|
||||
Serial.println("• Prefix matches: /service*");
|
||||
Serial.println("• Folder matches: /admin/*");
|
||||
Serial.println("• Extension matches: /images/*.jpg");
|
||||
Serial.println("• Case insensitive: /case (try /CASE, /Case)");
|
||||
#ifdef ASYNCWEBSERVER_REGEX
|
||||
Serial.println("• Regex matches: /user/123");
|
||||
#endif
|
||||
Serial.println("• Combined flags: /mixedcase*");
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Nothing to do here - the server handles everything asynchronously
|
||||
delay(1000);
|
||||
}
|
||||
Reference in New Issue
Block a user