Query string parameters

When handling incoming requests sometimes it is necessary to deal with query string parameter. For example, the following URL: http://example.com/location?country=canada&minrating=4&mindate=20180101 has 3 query parameters. Though one can obtain a raw query string from request header (see Request header) it is more convenient to use restinio::parse_query() function to get a key-value interface object to work with parameters:

query_string_params_t parse_query( string_view_t query_string );

Class query_string_params_t has the following interface:

class query_string_params_t
{
    // ...

    string_view_t operator [] ( string_view_t key ) const;

    optional_t< string_view_t > get_param( string_view_t key ) const noexcept;

    bool has( string_view_t key ) const noexcept;
    auto size() const noexcept;
    bool empty() const noexcept;

    auto begin() const noexcept;
    auto end() const noexcept;

    optional_t< string_view_t > tag() const noexcept;

    // ...
};

Parameters values are represented with string view. And of course one can cast parameters values with restinio::cast_to<T>().

A sample code for working with query string parameters:

restinio::request_handling_status_t handler( restinio::request_handle_t req )
{
    if( restinio::http_method_get() == req->header().method() )
    {
        std::ostringstream sout;
        sout << "GET request to '" << req->header().request_target() << "'\n";

        // Query params.
        const auto qp = restinio::parse_query( req->header().query() );

        if( 0 == qp.size() )
        {
            sout << "No query parameters.";
        }
        else
        {
            sout << "Query params ("<< qp.size() << "):\n";

            for( const auto p : qp )
            {
                sout << "'"<< p.first << "' => "<<  p.second << "'\n";
            }
        }

        if( qp.has( "debug" ) && qp[ "debug" ] == "true" )
        {
            std::cout << sout.str() << std::endl;
        }

        req->create_response()
            .append_header( restinio::http_field::server, "RESTinio query string params server" )
            .append_header_date_field()
            .append_header( restinio::http_field::content_type, "text/plain; charset=utf-8" )
            .set_body( sout.str() )
            .done();

        return restinio::request_accepted();
    }

    return restinio::request_rejected();
}

But it is more convenient to use special functions to get values from restinio key-value objects: value_or() and opt_value(), refer to Get values from RESTinio key-value containers section for details.

See also a full sample.

Additional features of parse_query

Parsing traits

The parse_query function is a template function with the default value for its single template parameter. A simple call of:

auto params = restinio::parse_query("name=A*&location=L*");

is just a shorthand for:

auto params = restinio::parse_query<
      restinio::parse_query_traits::restinio_defaults>("name=A*&location=L*");

A call to parse_query can be explicitly parametrized by a traits class. This traits class defines how various characters from a query-string should be iterpreted.

A traits parameter is necessary because there is no an official specificaton for query-string representation. The RFC3986 tells that any of characters from sub-delims character set can be used non-percent-encoded. But application/x-www-form-urlencoded allows only *, -, . and _. If JavaScript method encodeURIComponent is used for building the query-string, then symbols like *, (, ) and ! will be non-percent-encoded. And browsers like Chrome do not percent-encode symbols from gen-delims like [ and ]. A traits parameter tells RESTinio which symbols are enabled in non-percent-encoded form.

Since v.0.6.5 there are several ready to use traits in restinio::parse_query_traits namespace:

  • x_www_form_urlencoded. Follows application/x-www-form-urlencoded serializer and byte serializer rules. Only A..Z, a..z, 0..9, *, -, ., _ and + can be used non-percent-encoded. name=value pairs are separated by &;
  • javascript_compatible. Follows the behavior of JavaScript’s encodeURIComponent. Symbols A..Z, a..z, 0..9, !, ', (, ), *, -, ., _, ~ and + can be used non-percent-encoded. name=value pairs are separated by &;
  • restinio_defaults. Implements the behavior of early versions of RESTinio. Symbols A..Z, a..z, 0..9, -, ., _, ~ and + can be used non-percent-encoded. name=value pairs can be separated by & or ;;
  • relaxed. Allows much wider set of non-percent-encoded symbols.

A user can select an appropriate traits class or define its own.

For example, javascript compatible can be used to parse query strings formed by JavaScript code:

auto params = restinio::parse_query<
      restinio::parse_query_traits::javascript_compatible>("name=A*&location=L*");

Support for “web beacon”

Since v.0.4.9 RESTinio has the support for web beacon. It means that if a query string has the form http://example.com/resource?beacon then the value of beacon will be available via query_string_params_t::tag() method.

try_parse_query function template

Since v.0.6.5 there is another version of parse_query function: try_parse_query function template. It does the same thing as parse_query but doesn’t throw an exception if a query-string has illegal format.

The try_parse_query function returns expected type that contains query_string_params_t object if parsing was successful, or parse_query_failure_t otherwise.

Usage example:

auto parse_result = restinio::try_parse_query<
      restinio::parse_query_traits::relaxed>("a=[&b=]&c=(&d=)");
if(!parse_result) {
   std::cerr << "unable to parse query-string, error: "
         << parse_result.error().description() << std::endl;
   return ...;
}
else {
   const restinio::query_string_params_t & params = *parse_result;
   ... // Working with query-string content.
}

Unlike parse_query the try_parse_query requires explicit specification of traits class.

NOTE. The try_parse_query is not noexcept function. It doesn’t throw if query-string is ill-formed. But it can throw on other failures (like failed memory allocations).