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.

Route Handler

Route handlers have this signature:

route_result handler( route_params& rp );

After this chapter you can: dispatch HTTP requests to handlers based on method and path, chain handlers together, and control request flow.

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

route::send

Response is ready. Send it to the client.

route::next

Continue to the next handler in the chain.

route::next_route

Skip remaining handlers in this route, try the next route.

route::close

Close the connection after sending any response.

route::complete

Request fully handled; no response to send.

route::detach

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

/users

/users

Exact match

/users/:id

/users/42

Named parameter id = "42"

/files/*

/files/docs/readme.txt

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

case_sensitive

false

When true, /Users and /users are different routes.

strict

false

When true, /api and /api/ are different routes.

merge_params

false

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