Skip to main content

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.

Composition request filters sit directly in front of the individual composition handlers they are configured to intercept. Unlike endpoint filters — which run before the entire composition pipeline for every HTTP request — a composition filter is invoked only when its associated handler is about to execute. This makes them ideal for handler-specific concerns such as per-handler authorization checks, input validation, or logging that should only apply to one particular handler type, even when several handlers share the same route.
Composition request filters are available starting with v2.3.0 of ServiceComposer.AspNetCore.

Core interfaces

The composition filter contract is defined by two interfaces and one abstract attribute base class:
public interface ICompositionRequestFilter
{
    ValueTask<object> InvokeAsync(CompositionRequestFilterContext context,
        CompositionRequestFilterDelegate next);
}

// Marker interface — constrains the filter to a specific handler type
public interface ICompositionRequestFilter<T> : ICompositionRequestFilter;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public abstract class CompositionRequestFilterAttribute : Attribute, ICompositionRequestFilter
{
    public abstract ValueTask<object> InvokeAsync(CompositionRequestFilterContext context,
        CompositionRequestFilterDelegate next);
}
Both forms receive a CompositionRequestFilterContext (providing access to the current HttpContext via its HttpContext property) and a CompositionRequestFilterDelegate representing the next step in the filter pipeline. Your implementation must call next(context) to continue execution; omitting the call short-circuits the handler.

Defining a filter as an attribute

Create a class that inherits from CompositionRequestFilterAttribute. The attribute can be placed on the handler class itself or on individual handler methods:
public class SampleCompositionFilterAttribute : CompositionRequestFilterAttribute
{
    public override ValueTask<object> InvokeAsync(CompositionRequestFilterContext context, CompositionRequestFilterDelegate next)
    {
        return next(context);
    }
}
Then apply the attribute to the composition handler method you want to intercept:
public class SampleHandler : ICompositionRequestsHandler
{
    [SampleCompositionFilter]
    [HttpGet("/sample/{id}")]
    public Task Handle(HttpRequest request)
    {
        return Task.CompletedTask;
    }
}
The InvokeAsync implementation is responsible for invoking the next filter in the pipeline. If you do not call next(context), the handler will not execute and no further filters in the chain will run.

Defining a filter as a class

Create a class that implements ICompositionRequestFilter<T>, where T is the composition handler type to intercept:
public class SampleCompositionFilter : ICompositionRequestFilter<SampleHandler>
{
    public ValueTask<object> InvokeAsync(CompositionRequestFilterContext context, CompositionRequestFilterDelegate next)
    {
        return next(context);
    }
}
This filter is invoked only when SampleHandler is about to run, regardless of which route triggered it.
Filters defined as classes implementing ICompositionRequestFilter<T> are automatically discovered and registered in the DI container as transient by ServiceComposer’s assembly scanning. Because they are DI-registered, they can declare constructor dependencies and have them resolved normally.

Registration

Attribute-based filters require no separate registration step. Apply [SampleCompositionFilter] to the handler class or method and ServiceComposer picks it up during assembly scanning.

Composition filters vs endpoint filters

ServiceComposer provides two filter extension points. Choosing the right one depends on the scope of interception you need.

Use endpoint filters when…

  • The logic applies to the entire composed request regardless of which handlers are involved (e.g. request logging, timing, global validation).
  • You want to short-circuit the whole composition before any handler runs.
  • The logic is independent of specific handler types.

Use composition filters when…

  • The logic is specific to one particular composition handler type.
  • Different handlers on the same route need different pre/post processing (e.g. handler-specific authorization checks or input validation).
  • You want to use the attribute form to keep the filter declaration co-located with the handler method.
In short: endpoint filters are coarse-grained (the whole endpoint), while composition filters are fine-grained (a specific handler).

Build docs developers (and LLMs) love