Skip to main content

Pre & Post Processors

Processors are lightweight hooks that run before and after the handler. Unlike behaviors, they don't wrap the call — they are fire-and-forget side effects.

Execution Order

PreProcessors → [Pipeline Behaviors] → Handler → PostProcessors

Pre-Processors

Run before the handler executes. Useful for validation, logging, authentication checks.

For Requests

// IPreProcessor<TRequest> — runs before any request of this type
public class AuditPreProcessor : IPreProcessor<CreateOrderCommand>
{
private readonly IAuditService _audit;

public AuditPreProcessor(IAuditService audit)
{
_audit = audit;
}

public async Task Process(CreateOrderCommand request, CancellationToken ct)
{
await _audit.RecordAttemptAsync("CreateOrder", ct);
}
}

Generic Pre-Processor (All Requests)

public class LogRequestPreProcessor<TRequest> : IPreProcessor<TRequest>
{
private readonly ILogger<LogRequestPreProcessor<TRequest>> _logger;

public LogRequestPreProcessor(ILogger<LogRequestPreProcessor<TRequest>> logger)
{
_logger = logger;
}

public Task Process(TRequest request, CancellationToken ct)
{
_logger.LogDebug("Processing request: {RequestType}", typeof(TRequest).Name);
return Task.CompletedTask;
}
}

Post-Processors

Run after the handler executes. Useful for logging responses, publishing events, cache warming.

public class OrderCreatedPostProcessor
: IPostProcessor<CreateOrderCommand, Result<string>>
{
private readonly IValiMediator _mediator;

public OrderCreatedPostProcessor(IValiMediator mediator)
{
_mediator = mediator;
}

public async Task Process(
CreateOrderCommand request,
Result<string> response,
CancellationToken ct)
{
if (response.IsSuccess)
{
await _mediator.Publish(
new OrderCreatedEvent(response.Value, request.CustomerId), ct);
}
}
}

Auto-Discovery

Processors are automatically discovered from the scanned assemblies — no manual registration needed. Just implement the interface and register your assembly:

builder.Services.AddValiMediator(config =>
{
// All IPreProcessor<> and IPostProcessor<,> in this assembly are auto-registered
config.RegisterServicesFromAssemblyContaining<Program>();
});
note
  • IPreProcessor<TRequest>.Process() returns Task (async)
  • IPostProcessor<TRequest, TResponse>.Process() returns Task (async)
  • Both run for every matching request automatically
  • Multiple processors of the same type are supported

Processor vs Behavior

ProcessorBehavior
Can short-circuitNoYes (don't call next())
Has access to responsePost onlyYes (wraps entire call)
RegistrationAuto-discoveredManual
Use caseSimple side effectsComplex cross-cutting logic