Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 2f50357

Browse files
committedDec 5, 2019
Modify interface to query parameters. Close fhessel#62. Close fhessel#63
1 parent f80b961 commit 2f50357

File tree

9 files changed

+296
-116
lines changed

9 files changed

+296
-116
lines changed
 

‎examples/Parameter-Validation/Parameter-Validation.ino

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
#include <Arduino.h>
21
/**
32
* Example for the ESP32 HTTP(S) Webserver
43
*
@@ -115,13 +114,13 @@ void setup() {
115114
// This node will turn an LED on or of. It has two parameters:
116115
// 1) The ID of the LED (0..LEDCOUNT)
117116
// 2) The new state (0..1)
118-
// For more information on URL parameters in general, see the Parameters example.
117+
// For more information on path parameters in general, see the Parameters example.
119118
ResourceNode * nodeSwitch = new ResourceNode("/led/*/*", "POST", &handleSwitch);
120119

121120
// We want to use parameter validation. The ResourceNode class provides the method
122-
// addURLParamValidator() for that. This method takes two parameters:
121+
// addPathParamValidator() for that. This method takes two parameters:
123122
// 1) The index of the parameter that you want to validate, so for the first wildcard
124-
// in the URL pattern that has been specified above, it's 0, and for the second
123+
// in the route pattern that has been specified above, it's 0, and for the second
125124
// parameter it's 1.
126125
// 2) A function pointer that takes an std::string as parameter and returns a bool.
127126
// That bool should be true if the parameter is considered valid.
@@ -138,14 +137,14 @@ void setup() {
138137

139138
// First we will take care of the LED ID. This ID should be...
140139
// ... an unsigned integer ...
141-
nodeSwitch->addURLParamValidator(0, &validateUnsignedInteger);
140+
nodeSwitch->addPathParamValidator(0, &validateUnsignedInteger);
142141
// ... and within the range of known IDs.
143142
// We can treat the parameter safely as integer in this validator, as all validators
144143
// are executed in order and validateUnsignedInteger has been run before.
145-
nodeSwitch->addURLParamValidator(0, &validateLEDID);
144+
nodeSwitch->addPathParamValidator(0, &validateLEDID);
146145

147146
// The second parameter should either be 0 or 1. We use our custom validateLEDState() validator for this:
148-
nodeSwitch->addURLParamValidator(1, &validateLEDState);
147+
nodeSwitch->addPathParamValidator(1, &validateLEDState);
149148

150149
// Not found node
151150
ResourceNode * node404 = new ResourceNode("", "GET", &handle404);
@@ -232,13 +231,13 @@ void handleSwitch(HTTPRequest * req, HTTPResponse * res) {
232231
ResourceParameters * params = req->getParams();
233232

234233
// Get the LED that is requested.
235-
// Note that we can use the parameter directly without further validation, as we
234+
// Note that we can call stoi safely without further validation, as we
236235
// defined that is has to be an unsigned integer and must not be >LEDCOUNT-1
237-
LED * led = &myLEDs[params->getUrlParameterInt(0)];
236+
LED * led = &myLEDs[std::stoi(params->getPathParameter(0))];
238237

239238
// Set the state of the LED. The value of the parameter can only be "0" or "1" here,
240239
// otherwise the server would not have called the handler.
241-
led->setOn(params->getUrlParameter(1)!="0");
240+
led->setOn(params->getPathParameter(1)!="0");
242241

243242
// Redirect the user to the main page
244243
res->setStatusCode(303);
@@ -257,7 +256,7 @@ bool validateLEDState(std::string s) {
257256
// This function is a validator for the first parameter of the POST /led route.
258257
// We did check before that the parameter is an integer, now we check its range.
259258
bool validateLEDID(std::string s) {
260-
uint32_t id = parseUInt(s);
259+
uint32_t id = std::stoul(s);
261260
return id < LEDCOUNT;
262261
}
263262

‎examples/Parameters/Parameters.ino

Lines changed: 93 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ HTTPSServer secureServer = HTTPSServer(&cert);
5353
// We declare some handler functions (definition at the end of the file)
5454
void handleRoot(HTTPRequest * req, HTTPResponse * res);
5555
void handleSVG(HTTPRequest * req, HTTPResponse * res);
56-
void handleURLParam(HTTPRequest * req, HTTPResponse * res);
56+
void handleQueryDemo(HTTPRequest * req, HTTPResponse * res);
57+
void handlePathParam(HTTPRequest * req, HTTPResponse * res);
5758
void handle404(HTTPRequest * req, HTTPResponse * res);
5859

5960
void setup() {
@@ -72,27 +73,31 @@ void setup() {
7273

7374
// For every resource available on the server, we need to create a ResourceNode
7475
// The ResourceNode links URL and HTTP method to a handler function
75-
ResourceNode * nodeRoot = new ResourceNode("/", "GET", &handleRoot);
76-
ResourceNode * nodeSVG = new ResourceNode("/images/awesome.svg", "GET", &handleSVG);
77-
ResourceNode * node404 = new ResourceNode("", "GET", &handle404);
76+
ResourceNode * nodeRoot = new ResourceNode("/", "GET", &handleRoot);
77+
ResourceNode * nodeSVG = new ResourceNode("/images/awesome.svg", "GET", &handleSVG);
78+
ResourceNode * nodeQueryDemo = new ResourceNode("/queryparams", "GET", &handleQueryDemo);
79+
ResourceNode * node404 = new ResourceNode("", "GET", &handle404);
7880

79-
// URL params
81+
// Path parameters
8082
// If you want (for example) to return a specific instance of an object type by its ID
8183
// you can use URLs like /led/1, led/2, ... - And you do not need to register one Resource
82-
// Node per ID, but you can use wildcards in the URL definition. The following function
83-
// has to wildcards, and will match for example to /urlparam/foo/bar, where "foo" and "bar"
84+
// Node per ID, but you can use wildcards in the route definition. The following route
85+
// has two wildcards, and will match for example to /urlparam/foo/bar, where "foo" and "bar"
8486
// are accessible parameters in the handler function.
8587
// Note: The wildcards can only be used between slashes at the moment (so /urlparam* would
8688
// not work).
87-
ResourceNode * nodeURLParam = new ResourceNode("/urlparam/*/*", "GET", &handleURLParam);
89+
ResourceNode * nodeURLParam = new ResourceNode("/urlparam/*/*", "GET", &handlePathParam);
8890

8991
// Add the root node to the server
9092
secureServer.registerNode(nodeRoot);
9193

9294
// Add the SVG image
9395
secureServer.registerNode(nodeSVG);
9496

95-
// Add the URL param node
97+
// Query parameter demo
98+
secureServer.registerNode(nodeQueryDemo);
99+
100+
// Add the path parameter
96101
// Note: The order of nodes may become important here. If you have one node for "/led" (e.g. list of LEDs)
97102
// and one node for /led/* (LED details), you should register the non-parameterized version first. The server
98103
// follows a first-match policy. If you would register the details node first, a call to /led/ will be targetted
@@ -126,9 +131,11 @@ void handleRoot(HTTPRequest * req, HTTPResponse * res) {
126131
res->println("<!DOCTYPE html>");
127132
res->println("<html>");
128133
res->println("<head><title>Hello World!</title></head>");
134+
res->println("<style>.info{font-style:italic}</style>");
129135
res->println("<body>");
130136

131-
res->println("<h1>GET parameters</h1>");
137+
res->println("<h1>Query Parameters</h1>");
138+
res->println("<p class=\"info\">The parameters after the question mark in your URL.</p>");
132139

133140
// Show a form to select a color to colorize the faces
134141
// We pass the selection as get parameter "shades" to this very same page,
@@ -158,8 +165,8 @@ void handleRoot(HTTPRequest * req, HTTPResponse * res) {
158165
// Depending on the selection we show the images in a specific color shade
159166
// Default is dark gray.
160167
int r = 63, g = 63, b = 63;
161-
if (params->isRequestParameterSet(paramName)) {
162-
std::string paramVal = params->getRequestParameter(paramName);
168+
std::string paramVal;
169+
if (params->getQueryParameter(paramName, paramVal)) {
163170
if (paramVal == "red" || paramVal == "magenta" || paramVal == "yellow" || paramVal == "rainbow") {
164171
r = 128 + random(0, 128);
165172
}
@@ -178,19 +185,21 @@ void handleRoot(HTTPRequest * req, HTTPResponse * res) {
178185

179186
res->print("\" alt=\"Awesome!\" />");
180187
}
188+
res->println("<p>You'll find another demo <a href=\"/queryparams?a=42&b&c=13&a=hello\">here</a>.</p>");
181189

182-
// Link to the URL parameter demo
183-
res->println("<h1>URL parameters</h1>");
190+
// Link to the path parameter demo
191+
res->println("<h1>Path Parameters</h1>");
192+
res->println("<p class=\"info\">The parameters derived from placeholders in your path, like /foo/bar.</p>");
184193
res->println("<p>You'll find the demo <a href=\"/urlparam/foo/bar\">here</a>.</p>");
185194

186195
res->println("</body>");
187196
res->println("</html>");
188197
}
189198

190-
// This callback responds with an SVG image to a GET request. The icon is the awesome face.
199+
// This callback responds with an SVG image to a GET request. The icon is the "awesome face".
191200
// (borrowed from https://commons.wikimedia.org/wiki/File:718smiley.svg)
192201
//
193-
// If the color request parameter is set (so the URL is like awesome.svg?color=fede58), the
202+
// If the color query parameter is set (so the URL is like awesome.svg?color=fede58), the
194203
// background of our awesome face is changed.
195204
void handleSVG(HTTPRequest * req, HTTPResponse * res) {
196205
// Get access to the parameters
@@ -205,10 +214,11 @@ void handleSVG(HTTPRequest * req, HTTPResponse * res) {
205214
// Get request parameter (like awesome.svg?color=ff0000) and validate it
206215
std::string colorParamName = "color";
207216

208-
// Check that the parameter is set
209-
if (params->isRequestParameterSet(colorParamName)) {
210-
// Get the value of the parameter
211-
std::string requestColor = params->getRequestParameter(colorParamName);
217+
// Check that the parameter is set and retrieve it.
218+
// The getQueryParameter function will modify the second parameter, but only if the query
219+
// parameter is set.
220+
std::string requestColor;
221+
if (params->getQueryParameter(colorParamName, requestColor)) {
212222
// Check for correct length
213223
if (requestColor.length()==6) {
214224
bool colorOk = true;
@@ -247,10 +257,58 @@ void handleSVG(HTTPRequest * req, HTTPResponse * res) {
247257
res->print("</svg>");
248258
}
249259

260+
// This is a more generic demo for the query parameters. It makes use of the iterator
261+
// interface to access them, which is useful if you do not know the paramter names in
262+
// adavance.
263+
void handleQueryDemo(HTTPRequest * req, HTTPResponse * res) {
264+
// A word of warning: In this example, we use the query parameters and directly print
265+
// them into the HTML output. We do this to simplify the demo. NEVER do this in a
266+
// real application, as it allows cross-site-scripting.
267+
res->setHeader("Content-Type", "text/html");
268+
269+
res->println("<!DOCTYPE html>");
270+
res->println("<html>");
271+
res->println("<head>");
272+
res->println("<title>Query Parameter Demo</title>");
273+
res->println("</head>");
274+
res->println("<body>");
275+
res->println("<p>The following query paramters have been set:</p>");
276+
277+
// Start a table to display the parameters
278+
res->println("<table style=\"border:1px solid black collapse;\">");
279+
res->println("<tr><th>Key</th><th>Value</th></tr>");
280+
// Iterate over the parameters. For more information, read about the C++ standard template library,
281+
// especially about vectors and iterators.
282+
ResourceParameters *params = req->getParams();
283+
for(auto it = params->beginQueryParameters(); it != params->endQueryParameters(); ++it) {
284+
res->print("<tr><td>");
285+
286+
// The iterator yields std::pairs of std::strings. The first value contains the parameter key
287+
res->printStd((*it).first);
288+
res->print("</td><td>");
289+
290+
// and the second value contains the parameter value
291+
res->printStd((*it).second);
292+
res->println("</td></tr>");
293+
}
294+
res->println("</table>");
295+
296+
// You can retrieve the total parameter count from the parameters instance:
297+
res->print("<p>There are a total of ");
298+
res->print(params->getQueryParameterCount());
299+
res->print(" parameters, with ");
300+
res->print(params->getQueryParameterCount(true));
301+
res->println(" unique keys.</p>");
302+
303+
res->println("<p>Go <a href=\"/\">back to main page</a>.</p>");
304+
res->println("</body>");
305+
res->println("</html>");
306+
}
307+
250308
// This is a simple handler function that will show the content of URL parameters.
251309
// If you call for example /urlparam/foo/bar, you will get the parameter values
252310
// "foo" and "bar" provided by the ResourceParameters.
253-
void handleURLParam(HTTPRequest * req, HTTPResponse * res) {
311+
void handlePathParam(HTTPRequest * req, HTTPResponse * res) {
254312
// Get access to the parameters
255313
ResourceParameters * params = req->getParams();
256314

@@ -260,19 +318,26 @@ void handleURLParam(HTTPRequest * req, HTTPResponse * res) {
260318
// The url pattern is: /urlparam/*/*
261319
// This will make the content for the first parameter available on index 0,
262320
// and the second wildcard as index 1.
321+
// getPathParameter will - like getQueryParameter - write the value to the second parameter,
322+
// and return true, if the index is valid. Otherwise it returns false and leaves the second
323+
// parameter as it is.
263324

325+
std::string parameter1, parameter2;
264326
// Print the first parameter value
265-
res->print("Parameter 1: ");
266-
res->printStd(params->getUrlParameter(0));
327+
if (params->getPathParameter(0, parameter1)) {
328+
res->print("Parameter 1: ");
329+
res->printStd(parameter1);
330+
}
331+
332+
res->println();
267333

268334
// Print the second parameter value
269-
res->print("\nParameter 2: ");
270-
res->printStd(params->getUrlParameter(1));
335+
if (params->getPathParameter(1, parameter2)) {
336+
res->print("Parameter 2: ");
337+
res->printStd(parameter2);
338+
}
271339

272340
res->println("\n\nChange the parameters in the URL to see how they get parsed!");
273-
274-
// Note: If you have objects that are identified by an ID, you may also use
275-
// ResourceParameters::getUrlParameterInt(int) for convenience
276341
}
277342

278343
// For details to this function, see the Static-Page example

‎examples/REST-API/REST-API.ino

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,16 @@ char contentTypes[][2][32] = {
7878
#include <HTTPResponse.hpp>
7979
#include <util.hpp>
8080

81+
// The HTTPS Server comes in a separate namespace. For easier use, include it here.
82+
using namespace httpsserver;
83+
84+
SSLCert * getCertificate();
85+
void handleSPIFFS(HTTPRequest * req, HTTPResponse * res);
86+
void handleGetUptime(HTTPRequest * req, HTTPResponse * res);
87+
void handleGetEvents(HTTPRequest * req, HTTPResponse * res);
88+
void handlePostEvent(HTTPRequest * req, HTTPResponse * res);
89+
void handleDeleteEvent(HTTPRequest * req, HTTPResponse * res);
90+
8191
// We use the following struct to store GPIO events:
8292
#define MAX_EVENTS 20
8393
struct {
@@ -91,9 +101,6 @@ struct {
91101
int state;
92102
} events[MAX_EVENTS];
93103

94-
// The HTTPS Server comes in a separate namespace. For easier use, include it here.
95-
using namespace httpsserver;
96-
97104
// We just create a reference to the server here. We cannot call the constructor unless
98105
// we have initialized the SPIFFS and read or created the certificate
99106
HTTPSServer * secureServer;
@@ -504,7 +511,7 @@ void handlePostEvent(HTTPRequest * req, HTTPResponse * res) {
504511
void handleDeleteEvent(HTTPRequest * req, HTTPResponse * res) {
505512
// Access the parameter from the URL. See Parameters example for more details on this
506513
ResourceParameters * params = req->getParams();
507-
uint16_t eid = params->getUrlParameterInt(0);
514+
size_t eid = std::atoi(params->getPathParameter(0).c_str());
508515

509516
if (eid < MAX_EVENTS) {
510517
// Set the inactive flag

‎src/HTTPConnection.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -612,8 +612,12 @@ void validationMiddleware(HTTPRequest * req, HTTPResponse * res, std::function<v
612612
// Iterate over the validators and run them
613613
std::vector<HTTPValidator*> * validators = node->getValidators();
614614
for(std::vector<HTTPValidator*>::iterator validator = validators->begin(); valid && validator != validators->end(); ++validator) {
615-
std::string param = params->getUrlParameter((*validator)->_idx);
616-
valid = ((*validator)->_validatorFunction)(param);
615+
std::string param;
616+
if (params->getPathParameter((*validator)->_idx, param)) {
617+
valid = ((*validator)->_validatorFunction)(param);
618+
} else {
619+
valid = false;
620+
}
617621
}
618622

619623
if (valid) {

‎src/HTTPNode.cpp

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,29 +11,29 @@ namespace httpsserver {
1111
_validators = new std::vector<HTTPValidator*>();
1212

1313
// Count the parameters
14-
_urlParamCount = 0;
14+
_pathParamCount = 0;
1515
size_t idx = 0;
1616
while((idx = path.find("/*", idx)) != std::string::npos) {
17-
_urlParamCount+=1;
17+
_pathParamCount+=1;
1818
// If we don't do this, the same index will be found again... and again... and again...
1919
idx+=1;
2020
};
2121

2222
// Check if there are parameters
23-
if (_urlParamCount > 0) {
23+
if (_pathParamCount > 0) {
2424
// If there are parameters, store their indices
25-
_urlParamIdx = new size_t[_urlParamCount];
26-
for(int i = 0; i < _urlParamCount; i++) {
27-
_urlParamIdx[i] = path.find("/*", i==0 ? 0 : _urlParamIdx[i-1])+1;
25+
_pathParamIdx = new size_t[_pathParamCount];
26+
for(int i = 0; i < _pathParamCount; i++) {
27+
_pathParamIdx[i] = path.find("/*", i==0 ? 0 : _pathParamIdx[i-1])+1;
2828
}
2929
} else {
30-
_urlParamIdx = NULL;
30+
_pathParamIdx = NULL;
3131
}
3232
}
3333

3434
HTTPNode::~HTTPNode() {
35-
if (_urlParamIdx != NULL) {
36-
delete[] _urlParamIdx;
35+
if (_pathParamIdx != NULL) {
36+
delete[] _pathParamIdx;
3737
}
3838

3939
// Delete validator references
@@ -43,23 +43,23 @@ namespace httpsserver {
4343
delete _validators;
4444
}
4545

46-
bool HTTPNode::hasUrlParameter() {
47-
return _urlParamCount > 0;
46+
bool HTTPNode::hasPathParameter() {
47+
return _pathParamCount > 0;
4848
}
4949

5050
size_t HTTPNode::getParamIdx(uint8_t idx) {
51-
if (idx<_urlParamCount) {
52-
return _urlParamIdx[idx];
51+
if (idx<_pathParamCount) {
52+
return _pathParamIdx[idx];
5353
} else {
5454
return -1;
5555
}
5656
}
5757

58-
uint8_t HTTPNode::getUrlParamCount() {
59-
return _urlParamCount;
58+
size_t HTTPNode::getPathParamCount() {
59+
return _pathParamCount;
6060
}
6161

62-
void HTTPNode::addURLParamValidator(uint8_t paramIdx, const HTTPValidationFunction * validator) {
62+
void HTTPNode::addPathParamValidator(size_t paramIdx, const HTTPValidationFunction * validator) {
6363
_validators->push_back(new HTTPValidator(paramIdx, validator));
6464
}
6565

‎src/HTTPNode.hpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ class HTTPNode {
4343
/** Stores the type of the node (as we have not runtime type information by default) */
4444
const HTTPNodeType _nodeType;
4545

46-
bool hasUrlParameter();
47-
uint8_t getUrlParamCount();
46+
bool hasPathParameter();
47+
size_t getPathParamCount();
4848
size_t getParamIdx(uint8_t);
4949

5050
std::vector<HTTPValidator*> * getValidators();
@@ -58,11 +58,11 @@ class HTTPNode {
5858
*
5959
* @see ValidatorFunctions.hpp if you need some predefined templates for functions
6060
*/
61-
void addURLParamValidator(uint8_t paramIdx, const HTTPValidationFunction * validator);
61+
void addPathParamValidator(size_t paramIdx, const HTTPValidationFunction * validator);
6262

6363
private:
64-
uint8_t _urlParamCount;
65-
size_t * _urlParamIdx;
64+
size_t _pathParamCount;
65+
size_t * _pathParamIdx;
6666
std::vector<HTTPValidator*> * _validators;
6767
};
6868

‎src/ResourceParameters.cpp

Lines changed: 121 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -10,66 +10,157 @@ ResourceParameters::~ResourceParameters() {
1010

1111
}
1212

13-
bool ResourceParameters::isRequestParameterSet(std::string const &name) {
14-
for(auto reqParam = _reqParams.begin(); reqParam != _reqParams.end(); ++reqParam) {
15-
if ((*reqParam).first.compare(name)==0) {
13+
/**
14+
* @brief Checks whether a specific HTTPS query parameter is set.
15+
*
16+
* Query parameters are key-value pairs that are appended to the URI after a question mark.
17+
*
18+
* If the key exists (either as a value-less parameter or with a value), the function returns true.
19+
*
20+
* @param name The parameter to check
21+
* @return true iff the parameter exists
22+
*/
23+
bool ResourceParameters::isQueryParameterSet(std::string const &name) {
24+
for(auto queryParam = _queryParams.begin(); queryParam != _queryParams.end(); ++queryParam) {
25+
if ((*queryParam).first.compare(name)==0) {
1626
return true;
1727
}
1828
}
1929
return false;
2030
}
2131

22-
std::string ResourceParameters::getRequestParameter(std::string const &name) {
23-
for(auto reqParam = _reqParams.begin(); reqParam != _reqParams.end(); ++reqParam) {
24-
if ((*reqParam).first.compare(name)==0) {
25-
return (*reqParam).second;
32+
/**
33+
* @brief Returns an HTTP query parameter.
34+
*
35+
* Query parameters are key-value pairs that are appended to the URI after a question mark.
36+
*
37+
* The name parameter specifies the name of the query parameter to retrieve. If it is set,
38+
* the value is written to the value parameter and true is returned. If the parameter does
39+
* not exist, value is left unchanged and false is returned. If the parameter is used
40+
* without a value, an empty string is written to value and true is returned.
41+
*
42+
* @param name The name of the parameter to retrieve. If the parameter exists multiple times,
43+
* the first occurence is used for the value. Use beginQueryParameters() to retrieve all values.
44+
* @param value The target to write the value to, if the parameter exists.
45+
* @return true iff the parameter exists and the corresponding value has been written.
46+
*/
47+
bool ResourceParameters::getQueryParameter(std::string const &name, std::string &value) {
48+
for(auto queryParam = _queryParams.begin(); queryParam != _queryParams.end(); ++queryParam) {
49+
if ((*queryParam).first.compare(name)==0) {
50+
value=(*queryParam).second;
51+
return true;
2652
}
2753
}
28-
return "";
54+
return false;
55+
}
56+
57+
/**
58+
* @brief Returns the number of query parameters.
59+
*
60+
* Query parameters are key-value pairs that are appended to the URI after a question mark.
61+
*
62+
* @param unique If true, return the number of unique keys (using the same key multiple times
63+
* is counted only once). False by default, as checking for uniqueness is not efficient.
64+
* @return Number of query parameters
65+
*/
66+
size_t ResourceParameters::getQueryParameterCount(bool unique) {
67+
if (!unique) {
68+
return _queryParams.size();
69+
}
70+
size_t count = 0;
71+
for(auto a = _queryParams.begin(); a != _queryParams.end(); ++a) {
72+
bool exists = false;
73+
for(auto b = _queryParams.begin(); !exists && b != a; ++b) {
74+
exists = (*a).first.compare((*b).first)==0;
75+
}
76+
count += exists ? 0 : 1;
77+
}
78+
return count;
2979
}
3080

31-
uint16_t ResourceParameters::getRequestParameterInt(std::string const &name) {
32-
return parseInt(getRequestParameter(name));
81+
/**
82+
* @brief Provides iterator access to the query parameters
83+
*
84+
* Query parameters are key-value pairs that are appended to the URI after a question mark.
85+
*
86+
* If you want just a specific parameter, have a look at getQueryParameter()
87+
*
88+
* The iterator will yield pairs of std::string, of which the first value specifies the
89+
* query parameter key and the second value corresponds to the query parameters value.
90+
* If the entry is value-less, the second value will be the empty string.
91+
*
92+
* If the same key is used multiple times in the query, the iterator will yield it multiple
93+
* times, once for each occurence with the specific value.
94+
*
95+
* @return Iterator over std::pairs of std::strings that represent (key, value) pairs
96+
*/
97+
std::vector<std::pair<std::string,std::string>>::iterator ResourceParameters::beginQueryParameters() {
98+
return _queryParams.begin();
99+
}
100+
101+
/**
102+
* @brief Counterpart to beginQueryParameters() for iterating over query parameters
103+
*/
104+
std::vector<std::pair<std::string,std::string>>::iterator ResourceParameters::endQueryParameters() {
105+
return _queryParams.end();
33106
}
34107

35-
void ResourceParameters::setRequestParameter(std::string const &name, std::string const &value) {
108+
void ResourceParameters::setQueryParameter(std::string const &name, std::string const &value) {
36109
std::pair<std::string, std::string> param;
37110
param.first = name;
38111
param.second = value;
39-
_reqParams.push_back(param);
112+
_queryParams.push_back(param);
40113
}
41114

42115
/**
43-
* Returns an URL parameter as string.
116+
* @brief Checks for the existence of a path parameter and returns it as string.
44117
*
45-
* URL parameters are defined by resource nodes like object/?/property
46-
*
47-
* The parameter idx defines the index of the parameter, starting with 0.
118+
* Path parameters are defined by an asterisk as placeholder when specifying the path of
119+
* the ResourceNode and addressed by an index starting at 0 for the first parameter.
120+
*
121+
* For values of idx that have no matching placeholder, value is left unchanged and the
122+
* method will return false.
123+
*
124+
* @param idx Defines the index of the parameter to return, starting with 0.
125+
* @param value The value is written into this parameter.
126+
* @return true iff the value could be written.
48127
*/
49-
std::string ResourceParameters::getUrlParameter(uint8_t idx) {
50-
return _urlParams.at(idx);
128+
bool ResourceParameters::getPathParameter(size_t const idx, std::string &value) {
129+
if (idx < _pathParams.size()) {
130+
value = _pathParams.at(idx);
131+
return true;
132+
}
133+
return false;
51134
}
52135

53136
/**
54-
* Returns an URL parameter as int.
137+
* @brief Directly returns a path parameter
55138
*
56-
* URL parameters are defined by resource nodes like object/?/property
57-
*
58-
* The parameter idx defines the index of the parameter, starting with 0.
139+
* Path parameters are defined by an asterisk as placeholder when specifying the path of
140+
* the ResourceNode and addressed by an index starting at 0 for the first parameter.
141+
*
142+
* This method will return the parameter specified by the index. The caller is responsible
143+
* to assure that the index exists. Otherwise, an empty string will be returned.
144+
*
145+
* @param idx Defines the index of the parameter to return, starting with 0.
146+
* @return the value of the placeholder
59147
*/
60-
uint16_t ResourceParameters::getUrlParameterInt(uint8_t idx) {
61-
return parseInt(getUrlParameter(idx));
148+
std::string ResourceParameters::getPathParameter(size_t const idx) {
149+
if (idx < _pathParams.size()) {
150+
return _pathParams.at(idx);
151+
}
152+
return "";
62153
}
63154

64-
void ResourceParameters::resetUrlParameters() {
65-
_urlParams.clear();
155+
void ResourceParameters::resetPathParameters() {
156+
_pathParams.clear();
66157
}
67158

68-
void ResourceParameters::setUrlParameter(uint8_t idx, std::string const &val) {
69-
if(idx>=_urlParams.capacity()) {
70-
_urlParams.resize(idx + 1);
159+
void ResourceParameters::setPathParameter(size_t idx, std::string const &val) {
160+
if(idx>=_pathParams.capacity()) {
161+
_pathParams.resize(idx + 1);
71162
}
72-
_urlParams.at(idx) = val;
163+
_pathParams.at(idx) = val;
73164
}
74165

75166
} /* namespace httpsserver */

‎src/ResourceParameters.hpp

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,30 +14,44 @@
1414

1515
namespace httpsserver {
1616

17-
struct requestparam_t {std::string name; std::string value;};
17+
class ResourceResolver;
1818

1919
/**
20-
* \brief Class used to handle access to the URL parameters
20+
* @brief The ResourceParameters provide access to the parameters passed in the URI.
21+
*
22+
* There are two types of parameters: Path parameters and query parameters.
23+
*
24+
* Path parameters are the values that fill the asterisk placeholders in the route
25+
* definition of a ResourceNode.
26+
*
27+
* Query parameters are the key-value pairs after a question mark which can be added
28+
* to each request, either by specifying them manually or as result of submitting an
29+
* HTML form with a GET as method property.
2130
*/
2231
class ResourceParameters {
2332
public:
2433
ResourceParameters();
2534
virtual ~ResourceParameters();
2635

27-
bool isRequestParameterSet(std::string const &name);
28-
std::string getRequestParameter(std::string const &name);
29-
uint16_t getRequestParameterInt(std::string const &name);
30-
void setRequestParameter(std::string const &name, std::string const &value);
36+
bool isQueryParameterSet(std::string const &name);
37+
bool getQueryParameter(std::string const &name, std::string &value);
38+
std::vector<std::pair<std::string,std::string>>::iterator beginQueryParameters();
39+
std::vector<std::pair<std::string,std::string>>::iterator endQueryParameters();
40+
size_t getQueryParameterCount(bool unique=false);
41+
bool getPathParameter(size_t const idx, std::string &value);
42+
std::string getPathParameter(size_t const idx);
3143

32-
std::string getUrlParameter(uint8_t idx);
33-
uint16_t getUrlParameterInt(uint8_t idx);
34-
void resetUrlParameters();
35-
void setUrlParameterCount(uint8_t idx);
36-
void setUrlParameter(uint8_t idx, std::string const &val);
44+
protected:
45+
friend class ResourceResolver;
46+
void setQueryParameter(std::string const &name, std::string const &value);
47+
void resetPathParameters();
48+
void setPathParameter(size_t idx, std::string const &val);
3749

3850
private:
39-
std::vector<std::string> _urlParams;
40-
std::vector<std::pair<std::string, std::string>> _reqParams;
51+
/** Parameters in the path of the URL, the actual values for asterisk placeholders */
52+
std::vector<std::string> _pathParams;
53+
/** HTTP Query parameters, as key-value pairs */
54+
std::vector<std::pair<std::string, std::string>> _queryParams;
4155
};
4256

4357
} /* namespace httpsserver */

‎src/ResourceResolver.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ void ResourceResolver::resolveNode(const std::string &method, const std::string
6262
}
6363

6464
// Now we finally have name and value.
65-
params->setRequestParameter(name, value);
65+
params->setQueryParameter(name, value);
6666

6767
// Update reqparamIdx
6868
reqparamIdx = nextparamIdx;
@@ -73,7 +73,7 @@ void ResourceResolver::resolveNode(const std::string &method, const std::string
7373

7474
// Check whether a resource matches
7575
for(std::vector<HTTPNode*>::iterator node = _nodes->begin(); node != _nodes->end(); ++node) {
76-
params->resetUrlParameters();
76+
params->resetPathParameters();
7777
if ((*node)->_nodeType==nodeType) {
7878
if (
7979
// For handler functions, check the method declared with the node
@@ -82,7 +82,7 @@ void ResourceResolver::resolveNode(const std::string &method, const std::string
8282
((*node)->_nodeType==WEBSOCKET && method=="GET")
8383
) {
8484
const std::string nodepath = ((*node)->_path);
85-
if (!((*node)->hasUrlParameter())) {
85+
if (!((*node)->hasPathParameter())) {
8686
HTTPS_LOGD("Testing simple match on %s", nodepath.c_str());
8787

8888
// Simple matching, the node does not contain any resource parameters
@@ -98,7 +98,7 @@ void ResourceResolver::resolveNode(const std::string &method, const std::string
9898
bool didMatch = true;
9999
size_t urlIdx = 0; // Pointer how far the input url is processed
100100
size_t nodeIdx = 0; // Pointer how far the node url is processed
101-
for (int pIdx = 0; didMatch && pIdx < (*node)->getUrlParamCount(); pIdx++) {
101+
for (int pIdx = 0; didMatch && pIdx < (*node)->getPathParamCount(); pIdx++) {
102102
size_t pOffset = (*node)->getParamIdx(pIdx);
103103

104104
// First step: Check static part
@@ -115,15 +115,15 @@ void ResourceResolver::resolveNode(const std::string &method, const std::string
115115
// Second step: Grab the parameter value
116116
if (nodeIdx == nodepath.length()) {
117117
// Easy case: parse until end of string
118-
params->setUrlParameter(pIdx, urlDecode(resourceName.substr(urlIdx)));
118+
params->setPathParameter(pIdx, urlDecode(resourceName.substr(urlIdx)));
119119
} else {
120120
// parse until first char after the placeholder
121121
char terminatorChar = nodepath[nodeIdx];
122122
size_t terminatorPosition = resourceName.find(terminatorChar, urlIdx);
123123
if (terminatorPosition != std::string::npos) {
124124
// We actually found the terminator
125125
size_t dynamicLength = terminatorPosition-urlIdx;
126-
params->setUrlParameter(pIdx, urlDecode(resourceName.substr(urlIdx, dynamicLength)));
126+
params->setPathParameter(pIdx, urlDecode(resourceName.substr(urlIdx, dynamicLength)));
127127
urlIdx = urlIdx + dynamicLength;
128128
} else {
129129
// We did not find the terminator
@@ -164,7 +164,7 @@ void ResourceResolver::resolveNode(const std::string &method, const std::string
164164

165165
// If the resource did not match, configure the default resource
166166
if (!resolvedResource.didMatch() && _defaultNode != NULL) {
167-
params->resetUrlParameters();
167+
params->resetPathParameters();
168168
resolvedResource.setMatchingNode(_defaultNode);
169169
}
170170

0 commit comments

Comments
 (0)
Please sign in to comment.