Skip to content
This repository was archived by the owner on Jan 20, 2025. It is now read-only.

Commit 6a4e2f5

Browse files
Bmooijme-no-dev
authored andcommitted
Support route parameters with regex patterns (#560)
* Fix compile warnings * first test * Add regex path * Add simple example * add support for esp8266 * Update AsyncJson.h
1 parent 403752a commit 6a4e2f5

File tree

4 files changed

+101
-3
lines changed

4 files changed

+101
-3
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//
2+
// A simple server implementation with regex routes:
3+
// * serve static messages
4+
// * read GET and POST parameters
5+
// * handle missing pages / 404s
6+
//
7+
8+
#include <Arduino.h>
9+
#ifdef ESP32
10+
#include <WiFi.h>
11+
#include <AsyncTCP.h>
12+
#elif defined(ESP8266)
13+
#include <ESP8266WiFi.h>
14+
#include <ESPAsyncTCP.h>
15+
#endif
16+
#include <ESPAsyncWebServer.h>
17+
18+
AsyncWebServer server(80);
19+
20+
const char* ssid = "YOUR_SSID";
21+
const char* password = "YOUR_PASSWORD";
22+
23+
const char* PARAM_MESSAGE = "message";
24+
25+
void notFound(AsyncWebServerRequest *request) {
26+
request->send(404, "text/plain", "Not found");
27+
}
28+
29+
void setup() {
30+
31+
Serial.begin(115200);
32+
WiFi.mode(WIFI_STA);
33+
WiFi.begin(ssid, password);
34+
if (WiFi.waitForConnectResult() != WL_CONNECTED) {
35+
Serial.printf("WiFi Failed!\n");
36+
return;
37+
}
38+
39+
Serial.print("IP Address: ");
40+
Serial.println(WiFi.localIP());
41+
42+
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
43+
request->send(200, "text/plain", "Hello, world");
44+
});
45+
46+
// Send a GET request to <IP>/sensor/<number>
47+
server.on("^\\/sensor\\/([0-9]+)$", HTTP_GET, [] (AsyncWebServerRequest *request) {
48+
String sensorNumber = request->pathArg(0);
49+
request->send(200, "text/plain", "Hello, sensor: " + sensorNumber);
50+
});
51+
52+
// Send a GET request to <IP>/sensor/<number>/action/<action>
53+
server.on("^\\/sensor\\/([0-9]+)\\/action\//([a-zA-Z0-9]+)$", HTTP_GET, [] (AsyncWebServerRequest *request) {
54+
String sensorNumber = request->pathArg(0);
55+
String action = request->pathArg(1);
56+
request->send(200, "text/plain", "Hello, sensor: " + sensorNumber + ", with action: " + action);
57+
});
58+
59+
server.onNotFound(notFound);
60+
61+
server.begin();
62+
}
63+
64+
void loop() {
65+
}

src/ESPAsyncWebServer.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ class AsyncWebServerRequest {
129129
using File = fs::File;
130130
using FS = fs::FS;
131131
friend class AsyncWebServer;
132+
friend class AsyncCallbackWebHandler;
132133
private:
133134
AsyncClient* _client;
134135
AsyncWebServer* _server;
@@ -158,6 +159,7 @@ class AsyncWebServerRequest {
158159

159160
LinkedList<AsyncWebHeader *> _headers;
160161
LinkedList<AsyncWebParameter *> _params;
162+
LinkedList<String *> _pathParams;
161163

162164
uint8_t _multiParseState;
163165
uint8_t _boundaryPosition;
@@ -179,6 +181,7 @@ class AsyncWebServerRequest {
179181
void _onData(void *buf, size_t len);
180182

181183
void _addParam(AsyncWebParameter*);
184+
void _addPathParam(const char *param);
182185

183186
bool _parseReqHead();
184187
bool _parseReqHeader();
@@ -268,6 +271,8 @@ class AsyncWebServerRequest {
268271
bool hasArg(const char* name) const; // check if argument exists
269272
bool hasArg(const __FlashStringHelper * data) const; // check if F(argument) exists
270273

274+
const String& pathArg(size_t i) const;
275+
271276
const String& header(const char* name) const;// get request header value by name
272277
const String& header(const __FlashStringHelper * data) const;// get request header value by F(name)
273278
const String& header(size_t i) const; // get request header value by number

src/WebHandlerImpl.h

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
#ifndef ASYNCWEBSERVERHANDLERIMPL_H_
2222
#define ASYNCWEBSERVERHANDLERIMPL_H_
2323

24+
#include <string>
25+
#include <regex>
2426

2527
#include "stddef.h"
2628
#include <time.h>
@@ -67,9 +69,13 @@ class AsyncCallbackWebHandler: public AsyncWebHandler {
6769
ArRequestHandlerFunction _onRequest;
6870
ArUploadHandlerFunction _onUpload;
6971
ArBodyHandlerFunction _onBody;
72+
bool _isRegex;
7073
public:
71-
AsyncCallbackWebHandler() : _uri(), _method(HTTP_ANY), _onRequest(NULL), _onUpload(NULL), _onBody(NULL){}
72-
void setUri(const String& uri){ _uri = uri; }
74+
AsyncCallbackWebHandler() : _uri(), _method(HTTP_ANY), _onRequest(NULL), _onUpload(NULL), _onBody(NULL), _isRegex(false){}
75+
void setUri(const String& uri){
76+
_uri = uri;
77+
_isRegex = uri.startsWith("^") && uri.endsWith("$");
78+
}
7379
void setMethod(WebRequestMethodComposite method){ _method = method; }
7480
void onRequest(ArRequestHandlerFunction fn){ _onRequest = fn; }
7581
void onUpload(ArUploadHandlerFunction fn){ _onUpload = fn; }
@@ -83,7 +89,18 @@ class AsyncCallbackWebHandler: public AsyncWebHandler {
8389
if(!(_method & request->method()))
8490
return false;
8591

86-
if (_uri.length() && _uri.endsWith("*")) {
92+
if (_isRegex) {
93+
std::regex rgx(_uri.c_str());
94+
std::smatch matches;
95+
std::string s(request->url().c_str());
96+
if(std::regex_search(s, matches, rgx)) {
97+
for (size_t i = 1; i < matches.size(); ++i) { // start from 1
98+
request->_addPathParam(matches[i].str().c_str());
99+
}
100+
} else {
101+
return false;
102+
}
103+
} else if (_uri.length() && _uri.endsWith("*")) {
87104
String uriTemplate = String(_uri);
88105
uriTemplate = uriTemplate.substring(0, uriTemplate.length() - 1);
89106
if (!request->url().startsWith(uriTemplate))

src/WebRequest.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c)
5555
, _parsedLength(0)
5656
, _headers(LinkedList<AsyncWebHeader *>([](AsyncWebHeader *h){ delete h; }))
5757
, _params(LinkedList<AsyncWebParameter *>([](AsyncWebParameter *p){ delete p; }))
58+
, _pathParams(LinkedList<String *>([](String *p){ delete p; }))
5859
, _multiParseState(0)
5960
, _boundaryPosition(0)
6061
, _itemStartIndex(0)
@@ -80,6 +81,7 @@ AsyncWebServerRequest::~AsyncWebServerRequest(){
8081
_headers.free();
8182

8283
_params.free();
84+
_pathParams.free();
8385

8486
_interestingHeaders.free();
8587

@@ -230,6 +232,10 @@ void AsyncWebServerRequest::_addParam(AsyncWebParameter *p){
230232
_params.add(p);
231233
}
232234

235+
void AsyncWebServerRequest::_addPathParam(const char *p){
236+
_pathParams.add(new String(p));
237+
}
238+
233239
void AsyncWebServerRequest::_addGetParams(const String& params){
234240
size_t start = 0;
235241
while (start < params.length()){
@@ -912,6 +918,11 @@ const String& AsyncWebServerRequest::argName(size_t i) const {
912918
return getParam(i)->name();
913919
}
914920

921+
const String& AsyncWebServerRequest::pathArg(size_t i) const {
922+
auto param = _pathParams.nth(i);
923+
return param ? **param : SharedEmptyString;
924+
}
925+
915926
const String& AsyncWebServerRequest::header(const char* name) const {
916927
AsyncWebHeader* h = getHeader(String(name));
917928
return h ? h->value() : SharedEmptyString;

0 commit comments

Comments
 (0)