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:
Bob
2019-10-02 13:37:55 +02:00
committed by Me No Dev
parent 403752a942
commit 6a4e2f5329
4 changed files with 101 additions and 3 deletions

View 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() {
}

View File

@ -129,6 +129,7 @@ class AsyncWebServerRequest {
using File = fs::File;
using FS = fs::FS;
friend class AsyncWebServer;
friend class AsyncCallbackWebHandler;
private:
AsyncClient* _client;
AsyncWebServer* _server;
@ -158,6 +159,7 @@ class AsyncWebServerRequest {
LinkedList<AsyncWebHeader *> _headers;
LinkedList<AsyncWebParameter *> _params;
LinkedList<String *> _pathParams;
uint8_t _multiParseState;
uint8_t _boundaryPosition;
@ -179,6 +181,7 @@ class AsyncWebServerRequest {
void _onData(void *buf, size_t len);
void _addParam(AsyncWebParameter*);
void _addPathParam(const char *param);
bool _parseReqHead();
bool _parseReqHeader();
@ -268,6 +271,8 @@ class AsyncWebServerRequest {
bool hasArg(const char* name) const; // check if 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 __FlashStringHelper * data) const;// get request header value by F(name)
const String& header(size_t i) const; // get request header value by number

View File

@ -21,6 +21,8 @@
#ifndef ASYNCWEBSERVERHANDLERIMPL_H_
#define ASYNCWEBSERVERHANDLERIMPL_H_
#include <string>
#include <regex>
#include "stddef.h"
#include <time.h>
@ -67,9 +69,13 @@ class AsyncCallbackWebHandler: public AsyncWebHandler {
ArRequestHandlerFunction _onRequest;
ArUploadHandlerFunction _onUpload;
ArBodyHandlerFunction _onBody;
bool _isRegex;
public:
AsyncCallbackWebHandler() : _uri(), _method(HTTP_ANY), _onRequest(NULL), _onUpload(NULL), _onBody(NULL){}
void setUri(const String& uri){ _uri = uri; }
AsyncCallbackWebHandler() : _uri(), _method(HTTP_ANY), _onRequest(NULL), _onUpload(NULL), _onBody(NULL), _isRegex(false){}
void setUri(const String& uri){
_uri = uri;
_isRegex = uri.startsWith("^") && uri.endsWith("$");
}
void setMethod(WebRequestMethodComposite method){ _method = method; }
void onRequest(ArRequestHandlerFunction fn){ _onRequest = fn; }
void onUpload(ArUploadHandlerFunction fn){ _onUpload = fn; }
@ -83,7 +89,18 @@ class AsyncCallbackWebHandler: public AsyncWebHandler {
if(!(_method & request->method()))
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);
uriTemplate = uriTemplate.substring(0, uriTemplate.length() - 1);
if (!request->url().startsWith(uriTemplate))

View File

@ -55,6 +55,7 @@ AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c)
, _parsedLength(0)
, _headers(LinkedList<AsyncWebHeader *>([](AsyncWebHeader *h){ delete h; }))
, _params(LinkedList<AsyncWebParameter *>([](AsyncWebParameter *p){ delete p; }))
, _pathParams(LinkedList<String *>([](String *p){ delete p; }))
, _multiParseState(0)
, _boundaryPosition(0)
, _itemStartIndex(0)
@ -80,6 +81,7 @@ AsyncWebServerRequest::~AsyncWebServerRequest(){
_headers.free();
_params.free();
_pathParams.free();
_interestingHeaders.free();
@ -230,6 +232,10 @@ void AsyncWebServerRequest::_addParam(AsyncWebParameter *p){
_params.add(p);
}
void AsyncWebServerRequest::_addPathParam(const char *p){
_pathParams.add(new String(p));
}
void AsyncWebServerRequest::_addGetParams(const String& params){
size_t start = 0;
while (start < params.length()){
@ -912,6 +918,11 @@ const String& AsyncWebServerRequest::argName(size_t i) const {
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 {
AsyncWebHeader* h = getHeader(String(name));
return h ? h->value() : SharedEmptyString;