Skip to content

Commit

Permalink
Merge pull request #442 from CrowCpp/qs-from-req
Browse files Browse the repository at this point in the history
added creating qs from request body functionality + updated doc
  • Loading branch information
The-EDev committed May 23, 2022
2 parents 8bc0a4d + cd67331 commit c897101
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 46 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

## Description

Crow is a C++ microframework for running web services. It uses routing similar to Python's Flask which makes it easy to use. It is also extremely fast, beating multiple existing C++ frameworks as well as non C++ frameworks.
Crow is a C++ framework for creating HTTP or Websocket web services. It uses routing similar to Python's Flask which makes it easy to use. It is also extremely fast, beating multiple existing C++ frameworks as well as non C++ frameworks.

### Features
- Easy Routing (similar to flask).
Expand All @@ -27,6 +27,7 @@ Crow is a C++ microframework for running web services. It uses routing similar t
- Uses modern C++ (11/14)

### Still in development
- [Async support](https://github.com/CrowCpp/Crow/issues/258)
- [HTTP/2 support](https://github.com/crowcpp/crow/issues/8)

## Documentation
Expand Down
1 change: 1 addition & 0 deletions docs/guides/query-string.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ A query string is the part of the URL that comes after a `?` character, it is us
<br><br>

Crow supports query strings through `crow::request::url_params`. The object is of type `crow::query_string` and can has the following functions:<br>

## get(name)
Returns the value (as char*) based on the given key (or name). Returns `nullptr` if the key is not found.
## pop(name)
Expand Down
6 changes: 6 additions & 0 deletions docs/guides/routes.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,12 @@ Handlers can also use information from the request by adding it as a parameter `
You can also access the URL parameters in the handler using `#!cpp req.url_params.get("param_name");`. If the parameter doesn't exist, `nullptr` is returned.<br><br>
!!! note "Note &nbsp;&nbsp;&nbsp;&nbsp; <span class="tag">[:octicons-feed-tag-16: master](https://github.com/CrowCpp/Crow)</span>"
parameters inside the body can be parsed using `#!cpp req.get_body_params();`. which is useful for requests of type `application/x-www-form-urlencoded`. Its format is similar to `url_params`.
For more information on `crow::request` go [here](../../reference/structcrow_1_1request.html).<br><br>
### Response
Expand Down
10 changes: 6 additions & 4 deletions docs/overrides/home.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<meta property="og:title" content="CrowCpp"/>
<meta property="og:type" content="website" />
<meta property="og:description" content="A Fast and Easy to use microframework for the web."/>
<meta name="description" content="Crow is a C++ microframework for running web services. It uses routing similar to Python's Flask which makes it easy to use. It is also extremely fast, beating multiple existing C++ frameworks as well as non C++ frameworks.">
<meta name="description" content="Crow is a C++ framework for creating HTTP or Websocket web services. It uses routing similar to Python's Flask which makes it easy to use. It is also extremely fast, beating multiple existing C++ frameworks as well as non C++ frameworks.">
<meta property="og:image" content="assets/og_img.png" />
<meta property="og:url" content="https://crowcpp.org">
<meta property="twitter:card" content="summary_large_image">
Expand Down Expand Up @@ -148,6 +148,8 @@

<h1 style="text-align:center;">A Fast and Easy to use microframework for the web.</h1>

<hr>
<p style="text-align:center;">Crow is a C++ framework for creating HTTP or Websocket web services. It uses routing similar to Python's Flask which makes it easy to use. It is also extremely fast, beating multiple existing C++ frameworks as well as non C++ frameworks.</p>
<hr>

<section class="csection">
Expand Down Expand Up @@ -178,7 +180,7 @@ <h1 style="text-align:center;">A Fast and Easy to use microframework for the web

<section class="ssection">
<div class="sdescription">
<h2 style="text-align: center;">Easy to get started</h3>
<h2 style="text-align: center;">Easy to get started</h2>
</div>
<div class="scontent">
<div class="highlight"><pre id="__code_0"><span></span><button class="md-clipboard md-icon" title="Copy to clipboard" data-clipboard-target="#__code_0 > code"></button><code><span class="cp">#include</span> <span class="cpf">"crow.h"</span><span class="cp"></span>
Expand Down Expand Up @@ -208,14 +210,14 @@ <h2 style="text-align: center;">Easy to get started</h3>
</code></pre></div>
</div>
<div class="sdescription">
<h2 style="text-align: center;">JSON Support Built-in</h3>
<h2 style="text-align: center;">JSON Support Built-in</h2>
</div>

</section>

<section class="ssection">
<div class="sdescription">
<h2 style="text-align: center;">URL parameter support as well!</h3>
<h2 style="text-align: center;">URL parameter support as well!</h2>
</div>
<div class="scontent">
<div class="highlight"><pre id="__code_2"><span></span><button class="md-clipboard md-icon" title="Copy to clipboard" data-clipboard-target="#__code_2 > code"></button><code><span class="cp">CROW_ROUTE</span><span class="p">(</span><span class="n">app</span><span class="p">,</span><span class="s">"/hello/&lt;int&gt;"</span><span class="p">)</span>
Expand Down
9 changes: 9 additions & 0 deletions include/crow/http_request.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ namespace crow
return http_ver_major == major && http_ver_minor == minor;
}

/// Get the body as parameters in QS format.

///
/// This is meant to be used with requests of type "application/x-www-form-urlencoded"
const query_string get_body_params()
{
return query_string(body, false);
}

/// Send data to whoever made this request with a completion handler and return immediately.
template<typename CompletionHandler>
void post(CompletionHandler handler)
Expand Down
83 changes: 42 additions & 41 deletions include/crow/query_string.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,20 @@

namespace crow
{

// ----------------------------------------------------------------------------
// qs_parse (modified)
// https://github.com/bartgrantham/qs_parse
// ----------------------------------------------------------------------------
/* Similar to strncmp, but handles URL-encoding for either string */
int qs_strncmp(const char * s, const char * qs, size_t n);
int qs_strncmp(const char* s, const char* qs, size_t n);


/* Finds the beginning of each key/value pair and stores a pointer in qs_kv.
* Also decodes the value portion of the k/v pair *in-place*. In a future
* enhancement it will also have a compile-time option of sorting qs_kv
* alphabetically by key. */
int qs_parse(char * qs, char * qs_kv[], int qs_kv_size);
int qs_parse(char* qs, char* qs_kv[], int qs_kv_size, bool parse_url);


/* Used by qs_parse to decode the value portion of a k/v pair */
Expand Down Expand Up @@ -96,15 +97,15 @@ inline int qs_strncmp(const char * s, const char * qs, size_t n)
}


inline int qs_parse(char * qs, char * qs_kv[], int qs_kv_size)
inline int qs_parse(char* qs, char* qs_kv[], int qs_kv_size, bool parse_url = true)
{
int i, j;
char * substr_ptr;

for(i=0; i<qs_kv_size; i++) qs_kv[i] = NULL;

// find the beginning of the k/v substrings or the fragment
substr_ptr = qs + strcspn(qs, "?#");
substr_ptr = parse_url ? qs + strcspn(qs, "?#") : qs;
if (substr_ptr[0] != '\0')
substr_ptr++;
else
Expand Down Expand Up @@ -137,7 +138,7 @@ inline int qs_parse(char * qs, char * qs_kv[], int qs_kv_size)
#endif

return i;
}
}


inline int qs_decode(char * qs)
Expand Down Expand Up @@ -285,8 +286,9 @@ inline char * qs_scanvalue(const char * key, const char * qs, char * val, size_t
// ----------------------------------------------------------------------------


namespace crow
namespace crow
{
struct request;
/// A class to represent any data coming after the `?` in the request URL into key-value pairs.
class query_string
{
Expand All @@ -295,55 +297,54 @@ namespace crow

query_string()
{

}

query_string(const query_string& qs)
: url_(qs.url_)
query_string(const query_string& qs):
url_(qs.url_)
{
for(auto p:qs.key_value_pairs_)
for (auto p : qs.key_value_pairs_)
{
key_value_pairs_.push_back((char*)(p-qs.url_.c_str()+url_.c_str()));
key_value_pairs_.push_back((char*)(p - qs.url_.c_str() + url_.c_str()));
}
}

query_string& operator = (const query_string& qs)
query_string& operator=(const query_string& qs)
{
url_ = qs.url_;
key_value_pairs_.clear();
for(auto p:qs.key_value_pairs_)
for (auto p : qs.key_value_pairs_)
{
key_value_pairs_.push_back((char*)(p-qs.url_.c_str()+url_.c_str()));
key_value_pairs_.push_back((char*)(p - qs.url_.c_str() + url_.c_str()));
}
return *this;
}

query_string& operator = (query_string&& qs)
query_string& operator=(query_string&& qs)
{
key_value_pairs_ = std::move(qs.key_value_pairs_);
char* old_data = (char*)qs.url_.c_str();
url_ = std::move(qs.url_);
for(auto& p:key_value_pairs_)
for (auto& p : key_value_pairs_)
{
p += (char*)url_.c_str() - old_data;
}
return *this;
}


query_string(std::string url)
: url_(std::move(url))
query_string(std::string params, bool url = true):
url_(std::move(params))
{
if (url_.empty())
return;

key_value_pairs_.resize(MAX_KEY_VALUE_PAIRS_COUNT);

int count = qs_parse(&url_[0], &key_value_pairs_[0], MAX_KEY_VALUE_PAIRS_COUNT);
int count = qs_parse(&url_[0], &key_value_pairs_[0], MAX_KEY_VALUE_PAIRS_COUNT, url);
key_value_pairs_.resize(count);
}

void clear()
void clear()
{
key_value_pairs_.clear();
url_.clear();
Expand All @@ -352,38 +353,38 @@ namespace crow
friend std::ostream& operator<<(std::ostream& os, const query_string& qs)
{
os << "[ ";
for(size_t i = 0; i < qs.key_value_pairs_.size(); ++i) {
for (size_t i = 0; i < qs.key_value_pairs_.size(); ++i)
{
if (i)
os << ", ";
os << qs.key_value_pairs_[i];
}
os << " ]";
return os;

}

/// Get a value from a name, used for `?name=value`.

///
/// Note: this method returns the value of the first occurrence of the key only, to return all occurrences, see \ref get_list().
char* get (const std::string& name) const
char* get(const std::string& name) const
{
char* ret = qs_k2v(name.c_str(), key_value_pairs_.data(), key_value_pairs_.size());
return ret;
}

/// Works similar to \ref get() except it removes the item from the query string.
char* pop (const std::string& name)
char* pop(const std::string& name)
{
char* ret = get(name);
if (ret != nullptr)
{
for (unsigned int i = 0; i<key_value_pairs_.size(); i++)
for (unsigned int i = 0; i < key_value_pairs_.size(); i++)
{
std::string str_item(key_value_pairs_[i]);
if (str_item.substr(0, name.size()+1) == name+'=')
if (str_item.substr(0, name.size() + 1) == name + '=')
{
key_value_pairs_.erase(key_value_pairs_.begin()+i);
key_value_pairs_.erase(key_value_pairs_.begin() + i);
break;
}
}
Expand All @@ -395,14 +396,14 @@ namespace crow

///
/// Note: Square brackets in the above example are controlled by `use_brackets` boolean (true by default). If set to false, the example becomes `?name=value1,name=value2...name=valuen`
std::vector<char*> get_list (const std::string& name, bool use_brackets = true) const
std::vector<char*> get_list(const std::string& name, bool use_brackets = true) const
{
std::vector<char*> ret;
std::string plus = name + (use_brackets ? "[]" : "");
char* element = nullptr;

int count = 0;
while(1)
while (1)
{
element = qs_k2v(plus.c_str(), key_value_pairs_.data(), key_value_pairs_.size(), count++);
if (!element)
Expand All @@ -413,17 +414,17 @@ namespace crow
}

/// Similar to \ref get_list() but it removes the
std::vector<char*> pop_list (const std::string& name, bool use_brackets = true)
std::vector<char*> pop_list(const std::string& name, bool use_brackets = true)
{
std::vector<char*> ret = get_list(name, use_brackets);
if (!ret.empty())
{
for (unsigned int i = 0; i<key_value_pairs_.size(); i++)
for (unsigned int i = 0; i < key_value_pairs_.size(); i++)
{
std::string str_item(key_value_pairs_[i]);
if ((use_brackets ? (str_item.substr(0, name.size()+3) == name+"[]=") : (str_item.substr(0, name.size()+1) == name+'=')))
if ((use_brackets ? (str_item.substr(0, name.size() + 3) == name + "[]=") : (str_item.substr(0, name.size() + 1) == name + '=')))
{
key_value_pairs_.erase(key_value_pairs_.begin()+i--);
key_value_pairs_.erase(key_value_pairs_.begin() + i--);
}
}
}
Expand All @@ -436,12 +437,12 @@ namespace crow
/// For example calling `get_dict(yourname)` on `?yourname[sub1]=42&yourname[sub2]=84` would give a map containing `{sub1 : 42, sub2 : 84}`.
///
/// if your query string has both empty brackets and ones with a key inside, use pop_list() to get all the values without a key before running this method.
std::unordered_map<std::string, std::string> get_dict (const std::string& name) const
std::unordered_map<std::string, std::string> get_dict(const std::string& name) const
{
std::unordered_map<std::string, std::string> ret;

int count = 0;
while(1)
while (1)
{
if (auto element = qs_dict_name2kv(name.c_str(), key_value_pairs_.data(), key_value_pairs_.size(), count++))
ret.insert(*element);
Expand All @@ -452,17 +453,17 @@ namespace crow
}

/// Works the same as \ref get_dict() but removes the values from the query string.
std::unordered_map<std::string, std::string> pop_dict (const std::string& name)
std::unordered_map<std::string, std::string> pop_dict(const std::string& name)
{
std::unordered_map<std::string, std::string> ret = get_dict(name);
if (!ret.empty())
{
for (unsigned int i = 0; i<key_value_pairs_.size(); i++)
for (unsigned int i = 0; i < key_value_pairs_.size(); i++)
{
std::string str_item(key_value_pairs_[i]);
if (str_item.substr(0, name.size()+1) == name+'[')
if (str_item.substr(0, name.size() + 1) == name + '[')
{
key_value_pairs_.erase(key_value_pairs_.begin()+i--);
key_value_pairs_.erase(key_value_pairs_.begin() + i--);
}
}
}
Expand All @@ -472,7 +473,7 @@ namespace crow
std::vector<std::string> keys() const
{
std::vector<std::string> ret;
for (auto element: key_value_pairs_)
for (auto element : key_value_pairs_)
{
std::string str_element(element);
ret.emplace_back(str_element.substr(0, str_element.find('=')));
Expand All @@ -485,4 +486,4 @@ namespace crow
std::vector<char*> key_value_pairs_;
};

} // end namespace
} // namespace crow

0 comments on commit c897101

Please sign in to comment.