Front controller to jeden z najczęściej wykorzystywanych wzorców projektowych. W tym wpisie pokażę jak wykorzystać go w Pistache do stworzenia klasy zarządzającej routingiem.

Routing w Pistache

Http::Handler

Temat trasowania w Pistache można rozwiązać na kilka sposobów. Najłatwiejszym, pierwszym na jaki natkniesz się czytając dokumentację frameworka, jest zarejestrowanie klasy implementującej interfejsy Http::Handler metodą onRequest.

Metoda ta może wyglądać następująco:

void VersionHandler::onRequest(const Http::Request& request, Http::ResponseWriter response) {
    response.send(Http::Code::Ok, "DDS " DDS_VERSION);
}

A kod ją rejestrujący tak:

int main() {
    Http::listenAndServe<VersionHandler>("*:9080");
}

Routes::Get

Jednak, gdy chcemy udostępnić więcej niż jedną metodę, warto poszukać czegoś bardziej zaawansowanego. Tym krokiem na przód będzie stworzenie klasy kontrolującej routing. Klasa ta będzie wykorzystywała wbudowany w Pistache mechanizm routingu. Poszczególne żądania będą obsługiwane przez wyspecjalizowane klasy - handlery. Może to wyglądać na przykład tak:

#include "pistache/endpoint.h"
#include "handlers/VersionHandler.h"

class Endpoint
{
    
public:
    
    Endpoint(Net::Address addr)
        : httpEndpoint(std::make_shared<Net::Http::Endpoint>(addr))
    {
    }
    
    void init(size_t thr = 2) {
        auto opts = Net::Http::Endpoint::options()
            .threads(thr)
            .flags(Net::Tcp::Options::InstallSignalHandler);
        httpEndpoint->init(opts);
        setupRoutes();
    }

    void start() {
        httpEndpoint->setHandler(router.handler());
        httpEndpoint->serve();
    }
        
    void shutdown() {
        httpEndpoint->shutdown();
    }
        
    private:
        
        Rest::Router router;
        std::shared_ptr<Net::Http::Endpoint> httpEndpoint;
        VersionHandler version_handler;
        
        void setupRoutes() {
            using namespace Net::Rest;
            Routes::Get(router, "/version", Routes::bind(&VersionHandler::onRequest, &version_handler));

        }
};

Po tej zmianie kod rejestrujący może wyglądać w ten sposób:

int main() {
    
    Net::Port port(9080);

    int thr = 2;

    Net::Address addr(Net::Ipv4::any(), port);

    Endpoint stats(addr);

    stats.init(thr);
    stats.start();

    stats.shutdown();
}

Wykorzystując Routes::Get** należy zwrócić uwagę aby zmienić interfejs metody *onRequest:

void VersionHandler::onRequest(const Rest::Request& request, Net::Http::ResponseWriter response) {
    response.send(Http::Code::Ok, "DDS " DDS_VERSION);
}

I to wszystko. Po tej małej zmianie mamy do dyspozycji front controller, w którym możemy rejestrować w prosty sposób kolejne wyspecjalizowane handlery obsługujące żądania użytkowników naszej aplikacji.

Pokazane w tym wpisie przykłady są zaczerpnięte z aplikacji DDS.