Attribute-Based Routing (RTTI)
Kittox features an attribute-based routing system inspired by MARS / WiRL, the popular Delphi REST libraries. Handler classes are decorated with custom Delphi attributes; the framework discovers them via RTTI at startup and dispatches HTTP requests to the matching method automatically.
This architecture enables:
- Extensibility — applications register custom URL handlers without modifying the framework
- Modularity — each feature (chart, calendar, map) lives in its own unit with zero coupling to the core
- Enterprise separation — enterprise modules can be included or excluded by simply adding/removing a
usesreference
How it works
1. Declare a handler class
A handler is a plain Delphi class decorated with [TKXPath] on the class (base path) and on each public method (sub-path). HTTP method attributes ([TKXGET], [TKXPOST]) specify which requests the method handles.
unit Kitto.Web.Handler.Chart;
{$RTTI EXPLICIT METHODS([vcPublic, vcPublished])
PROPERTIES([vcPublic, vcPublished])}
type
[TKXPath('/kx/view/{ViewName}')]
TKXChartHandler = class
public
[TKXPath('/chart-data')]
[TKXGET]
procedure HandleChartData(
[TKXPathParam('ViewName')] const AViewName: string;
[TKXContext] ADataView: TKDataView);
end;RTTI directive required
The {$RTTI EXPLICIT ...} directive is mandatory in every handler unit. Without it, Delphi does not emit attribute metadata for methods and parameters, and the routing engine cannot discover the handler.
2. Register the handler
Registration happens in the initialization section — the same pattern used by TKXControllerRegistry for UI controllers:
initialization
TKXResourceRegistry.Instance.RegisterResource(TKXChartHandler);
finalization
TKXResourceRegistry.Instance.UnregisterResource(TKXChartHandler);At registration time, TKXResourceRegistry scans the class via RTTI:
- Reads
[TKXPath]from the class → base path (/kx/view/{ViewName}) - For each public method with
[TKXPath]+[TKXGET/POST]→ creates aTKXMethodInfo - For each parameter → reads
[TKXPathParam],[TKXQueryParam],[TKXFormParam], or[TKXContext] - Sorts all registered handlers by specificity (more literal segments = higher priority)
3. Request dispatch
When a request arrives, the TKXActivation engine (created per-request):
- Matches the URL against all registered path templates — literal segments match exactly,
{Param}segments capture any value - Fills method parameters from the appropriate source:
[TKXPathParam('ViewName')]→ extracted from the URL path[TKXQueryParam('key')]→ from the query string (?key=...)[TKXFormParam('_op')]→ from the POST body[TKXContext]→ resolved by type from the injection registry
- Invokes the method via
TRttiMethod.Invoke - Cleans up — the handler instance is created and destroyed within the same request
Attributes reference
| Attribute | Target | Description |
|---|---|---|
[TKXPath('/path/{Param}')] | Class, Method | URL path template with {Param} placeholders |
[TKXGET] | Method | Responds to HTTP GET |
[TKXPOST] | Method | Responds to HTTP POST |
[TKXANYAttribute] | Method | Responds to any HTTP method |
[TKXPathParam('Name')] | Parameter | Extracts {Name} from the URL path |
[TKXQueryParam('Name')] | Parameter | Extracts ?Name=value from the query string |
[TKXFormParam('Name')] | Parameter | Extracts from POST body (falls back to query string) |
[TKXContext] | Parameter | Dependency injection by type |
Dependency injection
Parameters decorated with [TKXContext] are resolved by their Delphi type from TKXInjectionRegistry. The framework pre-registers providers for common types:
| Parameter type | Injected value |
|---|---|
TKWebRequest | Current thread-local request |
TKWebResponse | Current thread-local response |
TKWebSession | Current thread-local session |
TKConfig | Application configuration singleton |
TKAuthenticator | Current authenticator |
TKDataView | Resolved from {ViewName} path parameter |
TKViewTable | MainTable of the resolved TKDataView |
Applications can register custom providers:
TKXInjectionRegistry.Instance.RegisterProvider(TypeInfo(TMyService),
function(const AType: TRttiType;
const AActivation: IKXActivationContext): TValue
begin
Result := TValue.From<TMyService>(TMyService.Create);
end);Dynamic script/CSS injection
Enterprise modules that require client-side libraries (e.g., Chart.js, EventCalendar) register them via TKXScriptRegistry in their initialization section:
// In Kitto.Html.ChartPanel.pas
initialization
TKXControllerRegistry.Instance.RegisterClass('ChartPanel',
TKXChartPanelController);
TKXScriptRegistry.Instance.RegisterScript('/js/chart.umd.min.js');// In Kitto.Html.CalendarPanel.pas
initialization
TKXControllerRegistry.Instance.RegisterClass('CalendarPanel',
TKXCalendarPanelController);
TKXScriptRegistry.Instance.RegisterStylesheet('/css/event-calendar.min.css');
TKXScriptRegistry.Instance.RegisterScript('/js/event-calendar.min.js');The page template (_Page.html) emits the registered tags via TemplatePro placeholders (dynamicStyles and dynamicScripts). If the enterprise module is not included in UseKitto.pas, no scripts are registered and no tags are emitted.
Custom application handlers
Applications can define their own URL handlers following the same pattern:
unit MyApp.Handler.Report;
{$RTTI EXPLICIT METHODS([vcPublic, vcPublished])
PROPERTIES([vcPublic, vcPublished])}
type
[TKXPath('/api/report/{ReportName}')]
TMyReportHandler = class
public
[TKXPath('/generate')]
[TKXPOST]
procedure Generate(
[TKXPathParam('ReportName')] const AName: string;
[TKXFormParam('format')] const AFormat: string;
[TKXContext] AConfig: TKConfig);
end;
implementation
uses Kitto.Web.Routing.Registry;
procedure TMyReportHandler.Generate(const AName, AFormat: string;
AConfig: TKConfig);
begin
// Custom report generation logic
end;
initialization
TKXResourceRegistry.Instance.RegisterResource(TMyReportHandler);
finalization
TKXResourceRegistry.Instance.UnregisterResource(TMyReportHandler);Include the unit in your application's UseKitto.pas or .dpr file — no framework modification required.
Module structure
The UseKitto.pas pattern controls which modules are active:
unit UseKitto;
interface
uses
Kitto.Html.All, // Core controllers (Apache 2.0)
Kitto.Web.Enterprise, // Enterprise modules (AGPL-3.0 / Commercial)
Kitto.Auth.DB; // Pluggable authenticator
implementation
end.Without Kitto.Web.Enterprise:
- No Chart.js, EventCalendar, or Google Maps JS loaded in the browser
- No enterprise data handlers registered
- No enterprise controller classes available
- Application compiles and runs with core features only
Architecture units
| Unit | Description |
|---|---|
Kitto.Web.Routing.Attributes | Attribute classes (TKXPath, TKXGET, etc.) |
Kitto.Web.Routing.Registry | TKXResourceRegistry — scans RTTI, caches method info |
Kitto.Web.Routing.Injection | TKXInjectionRegistry — resolves [TKXContext] by type |
Kitto.Web.Routing.Activation | TKXActivation — URL matching, parameter filling, RTTI invoke |
Kitto.Web.Routing.Route | TKXRoutingRoute — bridge into the existing route chain |
Kitto.Web.Routing.Providers | Default injection providers (Request, Session, Config, etc.) |
Kitto.Web.Routing.Scripts | TKXScriptRegistry — dynamic JS/CSS injection |
Kitto.Web.Routing.Session | IKXSessionProvider — session transport abstraction (cookie/JWT) |
See also
- Routing and Request Flow — complete URL routing reference
- Kittox Enterprise — enterprise feature matrix
- Licensing — Open Core licensing model
