Composition handlers are the building blocks of ServiceComposer. Each handler is a small, autonomous class that handles one slice of data for a given HTTP route — for example, the Sales service contributes pricing while the Marketing service contributes product descriptions. ServiceComposer discovers all matching handlers at startup and runs them in parallel, then merges their outputs into a single JSON response. No handler knows anything about the others.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/ServiceComposer/ServiceComposer.AspNetCore/llms.txt
Use this file to discover all available pages before exploring further.
The ICompositionRequestsHandler interface
Every composition handler implements ICompositionRequestsHandler, which defines a single method:
HttpRequest parameter gives the handler access to the full incoming request — body, route values, query string, headers, and the HttpContext. Each handler writes its own data slice onto the shared dynamic view model retrieved via request.GetComposedResponseModel().
Routing handlers to URLs
Handlers are decorated with standard ASP.NET routing attributes ([HttpGet], [HttpPost], [HttpPut], [HttpDelete], [HttpPatch]) to declare which route they respond to. Multiple handlers across different assemblies can target the exact same route template — they all run in parallel for every matching request.
Create a handler class library
Add a class library project per service (e.g.
Sales.ViewModelComposition) and add a package reference to ServiceComposer.AspNetCore.Implement ICompositionRequestsHandler
Create a class that implements the interface and decorate it with an
[Http*] attribute matching the route it handles.Gateway setup
Configure ServiceComposer once in the gateway’sProgram.cs:
AddViewModelComposition() triggers assembly scanning. MapCompositionHandlers() registers a composition endpoint for every route covered by at least one handler.
Writing handlers that share a route
The following two handlers both targetGET /product/{id}. ServiceComposer calls them concurrently via Task.WhenAll and each writes its own properties to the shared view model.
Sales handler — contributes price and availability:
GET /product/1 returns:
Assembly scanning
ServiceComposer automatically scans all loaded assemblies at startup. Any class that implementsICompositionRequestsHandler is registered as a transient component in the DI container and mapped to its declared route(s). No explicit registration is required — referencing the assembly is enough.
In production, handler libraries are typically distributed as NuGet packages and referenced from the gateway project:
Contract-less composition handlers
Available since v4.2.0-beta.1 Contract-less handlers let you write composition logic using familiar controller-action syntax, without implementingICompositionRequestsHandler on every class. Decorate the class with [CompositionHandler] and its methods with [Http*] attributes:
[CompositionHandler] class.
A C# source generator reads each [Http*]-decorated method at compile time and emits a concrete ICompositionRequestsHandler wrapper class. Services can be injected via [FromServices]:
Contract-less handler methods must be
public or internal, return Task, and be decorated with exactly one [Http*] attribute. Supporting multiple [Http*] attributes on the same method is a known limitation.Rules for contract-less handler methods
Access modifier
Must be
public or internal.Return type
Must return
Task.Routing
Must carry exactly one
[Http*] attribute.DI injection
Parameters marked
[FromServices] are resolved from the DI container at request time.Thread safety
BecauseGetComposedResponseModel() returns a dynamic ExpandoObject-backed instance, adding two different properties concurrently is safe — but setting the same property from two handlers is not. Design your handler boundaries so that no two handlers ever write to the same view model property.