Chain express routers
Feature request.
Building a server that has to support multiple restful APIs it's nice to keep things modular. It would be nice to be able to run a server built with one request handler that dispatches to the other routers to handle individual APIs. This would allow for better separation, as apposed to having a "god class" with knowledge of everything.
Attempt at an example:
namespace rr = restinio::router;
using router_t = rr::express_router_t<>;
auto getFooApiRouter(){
auto router = std::make_unique< router_t >();
router->http_get( "/:version" , &fooVersion );
router->http_get( "/:version/one" , &fooOne );
router->http_put( "/:version/one" , &fooOne );
//... etc
return router;
}
auto getBarApiRouter(){
auto router = std::make_unique< router_t >();
router->http_get( "/:version" , &barVersion );
router->http_get( "/:version/thing" , &barThing );
router->http_put( "/:version/buzz" , &barBuzz );
//... etc
return router;
}
auto router = std::make_unique< router_t >();
router->http_get( "/", &rootPage );
router->add_routes( "/foo" , getFooApiRouter() );
router->add_routes( "/bar" , getBarApiRouter() );
restinio::run(
restinio::on_this_thread< traits_t >()
.address( "localhost" )
.request_handler( router ) );
I wanted to see if it was possible at all, tried this prince-chrismc/restinio@6a9edbd7828ff75d61c7b52c2d71ca62d835222f and prince-chrismc/restinio@e9b74ec8aafef9cfad5fd865d15373bcf7900a0b, which seems like a sufficient solution to preferred concept. The individual API routers, would need the full path but I could live with that.
Hi!
I think that this particular task can be solved without modifying RESTinio's code. The one solution is:
void defineFooApiRouter(router_t & router, string_view & root) {
router.http_get( fmt::format("{}/:version", root) , &fooVersion );
router.http_get( fmt::format("{}/:version/one", root) , &fooOne );
router.http_put( fmt::format("{}/:version/one", root) , &fooOne );
//... etc
}
void defineBarApiRouter(router_t & router, string_view & root) {
router.http_get( fmt::format("{}/:version", root), &barVersion );
router.http_get( fmt::format("{}/:version/thing", root), &barThing );
router.http_put( fmt::format("{}/:version/buzz", root), &barBuzz );
//... etc
return router;
}
auto router = std::make_unique< router_t >();
router->http_get( "/", &rootPage );
defineFooApiRouter(*router, "/foo");
defineBarApiRouter(*router, "/bar");
restinio::run(
restinio::on_this_thread< traits_t >()
.address( "localhost" )
.request_handler( router ) );
However, I think there are several flaws in the current routing mechanism and I have some thoughts about fixing them.
One of the flaws is the repetition of several actions during defining and handling paths. A very simple example of what can be seen here. I have to repeat expressions like /:value(\d+):multiplier([MmKkBb]?) and calls to extract_chunk_size(params).
It would be great to have some way to avoid that and to receive more type checking. Unfortunately, I don't know yet how it can look like. Maybe something like this:
struct chunk_size_info {
size_t value_;
optional<char> multiplier_;
size_t actual_value() const noexcept { ... }
};
struct chunk_count_info {
size_t count_;
};
auto chunk_size_fragment = restinio::router::make_path_fragment<chunk_size_info>(
&chunk_size_info::value_, &chunk_size_info::multiplier);
auto chunk_count_fragment = restinio::router::make_path_fragment<chunk_count_info>(
&chunk_count_info);
...
router->http_get(root / chunk_size_fragment, ...);
router->http_get(root / chunk_size_fragment / chunk_count_fragment, ...);
It seems that if that approach will be implemented it can solve the your task too.
Anyway, thanks for the feature requests!
Thank you very much for the quick response.
I love the syntax you are proposing root / path_fragment / endpoint would be very descriptive.
I have opted for using you're proposal to extend multiple APIs. The suggestion is greatly appreciated =)
@lpc921
I think it's necessary to do something like that:
auto router = std::make_unique<router_t>();
... // Setup handlers for the router.
restinio::run(restinio::on_this_thread<traits_t>()
.address("localhost")
.request_handler(std::move(router)));