Server
This library provides an Express.js-style request dispatcher for HTTP servers. The interface is Sans-I/O: it handles routing and response generation without performing network operations. A separate I/O framework such as Boost.Beast2 manages connections and drives the protocol.
Router
router is a class template that implements request routing. It stores a
collection of routes, each with a path pattern, HTTP method, and one or more
handlers. Callers (typically a framework) use the router to dispatch an HTTP
request to a handler.
Overview
The router is an Express.js-style request dispatcher. You register handlers for path patterns and HTTP methods, then dispatch incoming requests. The router matches the request against registered routes and invokes the appropriate handlers in order.
#include <boost/http_proto.hpp>
using namespace boost::http_proto;
basic_router<route_params> router;
router.add(method::get, "/hello",
[](route_params& p)
{
p.status(status::ok);
p.set_body("Hello, world!");
return route::send;
});
The library provides route_params as the standard parameters type. It
contains the request, response, URL, and other context needed by handlers.
Handlers
A handler is any callable that accepts a reference to the params object and
returns a route_result:
route_result handler(route_params& p);
The return value tells the router what to do next:
| Value | Meaning |
|---|---|
|
Response is ready. Send it to the client. |
|
Continue to the next handler in the chain. |
|
Skip remaining handlers in this route, try the next route. |
|
Close the connection after sending any response. |
|
Request fully handled; no response to send. |
|
Handler took ownership of the session (advanced). |
Most handlers return route::send when they produce a response, or
route::next when they perform setup work and defer to later handlers.
Adding Routes
Use add() to register a handler for a specific HTTP method and path:
router.add(method::get, "/users", get_users);
router.add(method::post, "/users", create_user);
router.add(method::get, "/users/:id", get_user);
router.add(method::put, "/users/:id", update_user);
router.add(method::delete_, "/users/:id", delete_user);
Use all() to match any HTTP method:
router.all("/status", check_status);
Fluent Route Interface
The route() method returns a fluent interface for registering multiple
handlers on the same path:
router.route("/users/:id")
.add(method::get, get_user)
.add(method::put, update_user)
.add(method::delete_, delete_user)
.all(log_access);
This is equivalent to calling add() separately for each method, but more
concise when a path has multiple method handlers.
Dispatching Requests
Call dispatch() to route a request:
route_params p;
// ... populate p.req, p.url from parsed request ...
route_result rv = router.dispatch(method::get, p.url, p);
if(rv == route::send)
{
// p.res contains the response to send
}
else if(rv == route::next)
{
// No handler matched; send 404
}
The router tries each matching route in registration order. If a handler
returns route::next, the router continues to the next handler. If all
handlers return route::next, dispatch returns route::next to indicate
no handler produced a response.
Handler Chaining
Multiple handlers can be registered for the same route. They execute in
order until one returns something other than route::next:
router.add(method::get, "/admin",
[](route_params& p)
{
// Authentication check
if(!is_authenticated(p))
{
p.status(status::unauthorized);
p.set_body("Unauthorized");
return route::send;
}
return route::next;
},
[](route_params& p)
{
// Authorization check
if(!is_admin(p))
{
p.status(status::forbidden);
p.set_body("Forbidden");
return route::send;
}
return route::next;
},
[](route_params& p)
{
// Actual handler
p.status(status::ok);
p.set_body("Admin panel");
return route::send;
});
This pattern separates concerns: authentication, authorization, and business logic each have their own handler.
Path Patterns
Route paths support named parameters and wildcards:
| Pattern | Example URL | Matches |
|---|---|---|
|
|
Exact match |
|
|
Named parameter |
|
|
Wildcard suffix |
Path matching is case-insensitive by default. Use router_options to change
this behavior.
Router Options
Configure matching behavior when constructing the router:
basic_router<route_params> router(
router_options()
.case_sensitive(true) // Paths are case-sensitive
.strict(true)); // Trailing slash matters
| Option | Default | Description |
|---|---|---|
|
|
When true, |
|
|
When true, |
|
|
When true, inherit parameters from parent routers. |
Complete Example
#include <boost/http_proto.hpp>
using namespace boost::http_proto;
int main()
{
basic_router<route_params> router;
// Health check endpoint
router.add(method::get, "/health",
[](route_params& p)
{
p.status(status::ok);
p.set_body("OK");
return route::send;
});
// API routes
router.route("/api/echo")
.add(method::post,
[](route_params& p)
{
p.status(status::ok);
// Echo back the request body
return route::send;
})
.add(method::get,
[](route_params& p)
{
p.status(status::method_not_allowed);
return route::send;
});
// Dispatch a request
route_params p;
auto rv = router.dispatch(
method::get,
urls::url_view("/health"),
p);
// rv == route::send, p.res contains "OK"
}
See Also
-
Middleware - Path-based handler chains
-
Error Handling - Error and exception handlers
-
Route Parameters - The
route_paramsobject