Method-matchers for express- and easy_parser-based routers

Before v.0.6.6 a user can specify just one HTTP method for a request-handler, e.g.:

router->http_get(some_route_path, [](auto req, auto params) {...});
...
router->add_handler(
   restinio::http_method_lock(),
   another_route_path,
   [](auto req, auto params) {...});

But this limitation leads to unnecessary complexity in cases where a special HTTP status code should be returned if an illegal method was used for a resource. For example, a server allows to perform HTTP GET, HTTP POST and HTTP DELETE method to a particular route, but all other methods for the same route should be declined with HTTP 405 status code.

A new entity called method-matcher was introduced in v.0.6.6. This entity allows to specify a request handler for a set of HTTP methods. For example:

router->http_get(book_path, [](auto req, auto params) {...});
router->http_post(book_path, [](auto req, auto params) {...});
router->http_delete(book_path, [](auto req, auto params) {...});
// This request handler will be called for a request to "book_path"
// if HTTP method is not GET, POST, nor DELETE.
router->add_handler(
   restinio::router::none_of_methods(
      restinio::http_method_get(),
      restinio::http_method_post(),
      restinio::http_method_delete()),
   book_path,
   [](auto req, auto) {
      return req->create_response(restinio::status_method_not_allowed())
            .connection_close()
            .done();
   });

Ready-to-use method-matchers

RESTinio contains several ready-to-use method-matchers those can be split into two groups.

The first group is any-of matchers. They hold a list of HTTP method IDs and allows a method to be handled only if it is present in that list.

The second group is none-of matchers. They also hold a list of HTTP method IDs, but allow to handle a method only if it is not found in that list.

For example:

router->add_handler(
   // A request handler will be called only if
   // HTTP method is GET, POST or DELETE.
   restinio::router::any_of_methods(
      restinio::http_method_get(),
      restinio::http_method_post(),
      restinio::http_method_delete()),
   book_path,
   [](auto req, auto params) {...});

router->add_handler(
   // A request handler will be called for any HTTP method
   // except GET, POST or DELETE.
   restinio::router::none_of_methods(
      restinio::http_method_get(),
      restinio::http_method_post(),
      restinio::http_method_delete()),
   book_path,
   [](auto req, auto params) {...});

Those matchers can also be divided into two categories: the first one is matchers with a list of methods fixed at the compile-time, and the second is matchers with a dynamically expandable list of methods:

// This is a fixed size matcher.
restinio::router::any_of_methods(
   restinio::http_method_get(),
   restinio::http_method_head());
// This is also a fixed size matcher.
restinio::router::none_of_methods(
   restinio::http_method_post(),
   restinio::http_method_put(),
   restinio::http_method_delete());

// This is a dynamic size matcher.
restinio::router::dynamic_any_of_methods_matcher_t matcher1;
matcher1.add(restinio::http_method_get());
if(some_condition)
   matcher1.add(restinio::http_method_head());

// This is also a dynamic size matcher.
restinio::router::dynamic_none_of_methods_matcher_t matcher2;
matcher2.add(restinio::http_method_post());
if(some_condition)
   matcher2.add(restinio::http_method_put());
if(another_condition)
   matcher2.add(restinio::http_method_delete());

The main difference between those categories is the absence of dynamic memory allocation for fixed-size matchers. The size for a fixed-size matcher is automatically calculated by a compiler at the compile-time. Dynamic-size matchers can allocate some memory in add() methods.

A possibility to make own matchers

There is an interface of method-matchers in RESTinio with name method_matcher_t in restinio::router namespace. This interface has just one pure virtual method match().

If a user wants to make his/her own matcher type he/she can derive a class from method_matcher_t and implement match() method.

Method-matchers can be used for express- and easy_parser-based routers

Method-matchers are router-agnostic entities. So a user can use the same method-matcher for express- or easy_parser-based routers:

// Express-router.
expr_router->add_method(
   restinio::router::none_of_methods(
      restinio::http_method_get(),
      restinio::http_method_post(),
      restinio::http_method_delete()),
   "/books/:id(\d{1,10})/versions/:version",
   [](auto req, auto params) {...});

// Easy_parser-based router.
namespace epr = restinio::router::easy_parser_router;
epr_router->add_method(
   restinio::router::none_of_methods(
      restinio::http_method_get(),
      restinio::http_method_post(),
      restinio::http_method_delete()),
   epr::path_to_params(
      "/books/",
      epr::non_negative_decimal_number_p<std::uint64_t>(
         epr::expected_digits(1, 10)),
      "/versions/",
      epr::path_fragment_p()),
   [](auto req, auto book_id, const auto & version_id) {...});