forked from me-no-dev/ESPAsyncWebServer
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
This commit is contained in:
65
examples/regex_patterns/regex_patterns.ino
Normal file
65
examples/regex_patterns/regex_patterns.ino
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
//
|
||||||
|
// A simple server implementation with regex routes:
|
||||||
|
// * serve static messages
|
||||||
|
// * read GET and POST parameters
|
||||||
|
// * handle missing pages / 404s
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#ifdef ESP32
|
||||||
|
#include <WiFi.h>
|
||||||
|
#include <AsyncTCP.h>
|
||||||
|
#elif defined(ESP8266)
|
||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
#include <ESPAsyncTCP.h>
|
||||||
|
#endif
|
||||||
|
#include <ESPAsyncWebServer.h>
|
||||||
|
|
||||||
|
AsyncWebServer server(80);
|
||||||
|
|
||||||
|
const char* ssid = "YOUR_SSID";
|
||||||
|
const char* password = "YOUR_PASSWORD";
|
||||||
|
|
||||||
|
const char* PARAM_MESSAGE = "message";
|
||||||
|
|
||||||
|
void notFound(AsyncWebServerRequest *request) {
|
||||||
|
request->send(404, "text/plain", "Not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
|
||||||
|
Serial.begin(115200);
|
||||||
|
WiFi.mode(WIFI_STA);
|
||||||
|
WiFi.begin(ssid, password);
|
||||||
|
if (WiFi.waitForConnectResult() != WL_CONNECTED) {
|
||||||
|
Serial.printf("WiFi Failed!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.print("IP Address: ");
|
||||||
|
Serial.println(WiFi.localIP());
|
||||||
|
|
||||||
|
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||||
|
request->send(200, "text/plain", "Hello, world");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Send a GET request to <IP>/sensor/<number>
|
||||||
|
server.on("^\\/sensor\\/([0-9]+)$", HTTP_GET, [] (AsyncWebServerRequest *request) {
|
||||||
|
String sensorNumber = request->pathArg(0);
|
||||||
|
request->send(200, "text/plain", "Hello, sensor: " + sensorNumber);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Send a GET request to <IP>/sensor/<number>/action/<action>
|
||||||
|
server.on("^\\/sensor\\/([0-9]+)\\/action\//([a-zA-Z0-9]+)$", HTTP_GET, [] (AsyncWebServerRequest *request) {
|
||||||
|
String sensorNumber = request->pathArg(0);
|
||||||
|
String action = request->pathArg(1);
|
||||||
|
request->send(200, "text/plain", "Hello, sensor: " + sensorNumber + ", with action: " + action);
|
||||||
|
});
|
||||||
|
|
||||||
|
server.onNotFound(notFound);
|
||||||
|
|
||||||
|
server.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
}
|
@ -129,6 +129,7 @@ class AsyncWebServerRequest {
|
|||||||
using File = fs::File;
|
using File = fs::File;
|
||||||
using FS = fs::FS;
|
using FS = fs::FS;
|
||||||
friend class AsyncWebServer;
|
friend class AsyncWebServer;
|
||||||
|
friend class AsyncCallbackWebHandler;
|
||||||
private:
|
private:
|
||||||
AsyncClient* _client;
|
AsyncClient* _client;
|
||||||
AsyncWebServer* _server;
|
AsyncWebServer* _server;
|
||||||
@ -158,6 +159,7 @@ class AsyncWebServerRequest {
|
|||||||
|
|
||||||
LinkedList<AsyncWebHeader *> _headers;
|
LinkedList<AsyncWebHeader *> _headers;
|
||||||
LinkedList<AsyncWebParameter *> _params;
|
LinkedList<AsyncWebParameter *> _params;
|
||||||
|
LinkedList<String *> _pathParams;
|
||||||
|
|
||||||
uint8_t _multiParseState;
|
uint8_t _multiParseState;
|
||||||
uint8_t _boundaryPosition;
|
uint8_t _boundaryPosition;
|
||||||
@ -179,6 +181,7 @@ class AsyncWebServerRequest {
|
|||||||
void _onData(void *buf, size_t len);
|
void _onData(void *buf, size_t len);
|
||||||
|
|
||||||
void _addParam(AsyncWebParameter*);
|
void _addParam(AsyncWebParameter*);
|
||||||
|
void _addPathParam(const char *param);
|
||||||
|
|
||||||
bool _parseReqHead();
|
bool _parseReqHead();
|
||||||
bool _parseReqHeader();
|
bool _parseReqHeader();
|
||||||
@ -268,6 +271,8 @@ class AsyncWebServerRequest {
|
|||||||
bool hasArg(const char* name) const; // check if argument exists
|
bool hasArg(const char* name) const; // check if argument exists
|
||||||
bool hasArg(const __FlashStringHelper * data) const; // check if F(argument) exists
|
bool hasArg(const __FlashStringHelper * data) const; // check if F(argument) exists
|
||||||
|
|
||||||
|
const String& pathArg(size_t i) const;
|
||||||
|
|
||||||
const String& header(const char* name) const;// get request header value by name
|
const String& header(const char* name) const;// get request header value by name
|
||||||
const String& header(const __FlashStringHelper * data) const;// get request header value by F(name)
|
const String& header(const __FlashStringHelper * data) const;// get request header value by F(name)
|
||||||
const String& header(size_t i) const; // get request header value by number
|
const String& header(size_t i) const; // get request header value by number
|
||||||
|
@ -21,6 +21,8 @@
|
|||||||
#ifndef ASYNCWEBSERVERHANDLERIMPL_H_
|
#ifndef ASYNCWEBSERVERHANDLERIMPL_H_
|
||||||
#define ASYNCWEBSERVERHANDLERIMPL_H_
|
#define ASYNCWEBSERVERHANDLERIMPL_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
#include "stddef.h"
|
#include "stddef.h"
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
@ -67,9 +69,13 @@ class AsyncCallbackWebHandler: public AsyncWebHandler {
|
|||||||
ArRequestHandlerFunction _onRequest;
|
ArRequestHandlerFunction _onRequest;
|
||||||
ArUploadHandlerFunction _onUpload;
|
ArUploadHandlerFunction _onUpload;
|
||||||
ArBodyHandlerFunction _onBody;
|
ArBodyHandlerFunction _onBody;
|
||||||
|
bool _isRegex;
|
||||||
public:
|
public:
|
||||||
AsyncCallbackWebHandler() : _uri(), _method(HTTP_ANY), _onRequest(NULL), _onUpload(NULL), _onBody(NULL){}
|
AsyncCallbackWebHandler() : _uri(), _method(HTTP_ANY), _onRequest(NULL), _onUpload(NULL), _onBody(NULL), _isRegex(false){}
|
||||||
void setUri(const String& uri){ _uri = uri; }
|
void setUri(const String& uri){
|
||||||
|
_uri = uri;
|
||||||
|
_isRegex = uri.startsWith("^") && uri.endsWith("$");
|
||||||
|
}
|
||||||
void setMethod(WebRequestMethodComposite method){ _method = method; }
|
void setMethod(WebRequestMethodComposite method){ _method = method; }
|
||||||
void onRequest(ArRequestHandlerFunction fn){ _onRequest = fn; }
|
void onRequest(ArRequestHandlerFunction fn){ _onRequest = fn; }
|
||||||
void onUpload(ArUploadHandlerFunction fn){ _onUpload = fn; }
|
void onUpload(ArUploadHandlerFunction fn){ _onUpload = fn; }
|
||||||
@ -83,7 +89,18 @@ class AsyncCallbackWebHandler: public AsyncWebHandler {
|
|||||||
if(!(_method & request->method()))
|
if(!(_method & request->method()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (_uri.length() && _uri.endsWith("*")) {
|
if (_isRegex) {
|
||||||
|
std::regex rgx(_uri.c_str());
|
||||||
|
std::smatch matches;
|
||||||
|
std::string s(request->url().c_str());
|
||||||
|
if(std::regex_search(s, matches, rgx)) {
|
||||||
|
for (size_t i = 1; i < matches.size(); ++i) { // start from 1
|
||||||
|
request->_addPathParam(matches[i].str().c_str());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (_uri.length() && _uri.endsWith("*")) {
|
||||||
String uriTemplate = String(_uri);
|
String uriTemplate = String(_uri);
|
||||||
uriTemplate = uriTemplate.substring(0, uriTemplate.length() - 1);
|
uriTemplate = uriTemplate.substring(0, uriTemplate.length() - 1);
|
||||||
if (!request->url().startsWith(uriTemplate))
|
if (!request->url().startsWith(uriTemplate))
|
||||||
|
@ -55,6 +55,7 @@ AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c)
|
|||||||
, _parsedLength(0)
|
, _parsedLength(0)
|
||||||
, _headers(LinkedList<AsyncWebHeader *>([](AsyncWebHeader *h){ delete h; }))
|
, _headers(LinkedList<AsyncWebHeader *>([](AsyncWebHeader *h){ delete h; }))
|
||||||
, _params(LinkedList<AsyncWebParameter *>([](AsyncWebParameter *p){ delete p; }))
|
, _params(LinkedList<AsyncWebParameter *>([](AsyncWebParameter *p){ delete p; }))
|
||||||
|
, _pathParams(LinkedList<String *>([](String *p){ delete p; }))
|
||||||
, _multiParseState(0)
|
, _multiParseState(0)
|
||||||
, _boundaryPosition(0)
|
, _boundaryPosition(0)
|
||||||
, _itemStartIndex(0)
|
, _itemStartIndex(0)
|
||||||
@ -80,6 +81,7 @@ AsyncWebServerRequest::~AsyncWebServerRequest(){
|
|||||||
_headers.free();
|
_headers.free();
|
||||||
|
|
||||||
_params.free();
|
_params.free();
|
||||||
|
_pathParams.free();
|
||||||
|
|
||||||
_interestingHeaders.free();
|
_interestingHeaders.free();
|
||||||
|
|
||||||
@ -230,6 +232,10 @@ void AsyncWebServerRequest::_addParam(AsyncWebParameter *p){
|
|||||||
_params.add(p);
|
_params.add(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AsyncWebServerRequest::_addPathParam(const char *p){
|
||||||
|
_pathParams.add(new String(p));
|
||||||
|
}
|
||||||
|
|
||||||
void AsyncWebServerRequest::_addGetParams(const String& params){
|
void AsyncWebServerRequest::_addGetParams(const String& params){
|
||||||
size_t start = 0;
|
size_t start = 0;
|
||||||
while (start < params.length()){
|
while (start < params.length()){
|
||||||
@ -912,6 +918,11 @@ const String& AsyncWebServerRequest::argName(size_t i) const {
|
|||||||
return getParam(i)->name();
|
return getParam(i)->name();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const String& AsyncWebServerRequest::pathArg(size_t i) const {
|
||||||
|
auto param = _pathParams.nth(i);
|
||||||
|
return param ? **param : SharedEmptyString;
|
||||||
|
}
|
||||||
|
|
||||||
const String& AsyncWebServerRequest::header(const char* name) const {
|
const String& AsyncWebServerRequest::header(const char* name) const {
|
||||||
AsyncWebHeader* h = getHeader(String(name));
|
AsyncWebHeader* h = getHeader(String(name));
|
||||||
return h ? h->value() : SharedEmptyString;
|
return h ? h->value() : SharedEmptyString;
|
||||||
|
Reference in New Issue
Block a user