Skip to content

Pre- and post-processors ​

Processors are lighter than behaviors: they do not wrap the handler, they run next to it.

  • IRequestPreProcessor<TRequest> runs before the handler.
  • IRequestPostProcessor<TRequest, TResponse> runs after the handler returns successfully, and receives the response.
csharp
public sealed class ValidateUserId : IRequestPreProcessor<GetUser>
{
    public Task Process(GetUser request, CancellationToken cancellationToken)
    {
        if (request.Id <= 0) throw new ArgumentOutOfRangeException(nameof(request.Id));
        return Task.CompletedTask;
    }
}

public sealed class AuditUserRead(IAuditLog audit) : IRequestPostProcessor<GetUser, User>
{
    public Task Process(GetUser request, User response, CancellationToken cancellationToken)
        => audit.RecordAsync($"read user {response.Id}", cancellationToken);
}

Register ​

Processors are opt-in. Either register them one by one:

csharp
services.AddAnyMediator(cfg =>
{
    cfg.RegisterServicesFromAssemblyContaining<Program>();
    cfg.AddRequestPreProcessor<ValidateUserId>();
    cfg.AddRequestPostProcessor<AuditUserRead>();
});

or let the assembly scan find them:

csharp
cfg.AutoRegisterRequestProcessors = true;

Open generic processors that apply to every request are registered with AddOpenRequestPreProcessor(typeof(P<>)) and AddOpenRequestPostProcessor(typeof(P<,>)).

When they run ​

  • Several processors for the same request run one after another, in registration order.
  • Pre-processors run inside your pipeline behaviors, just before the handler.
  • Post-processors run just after the handler, before your behaviors see the response.
  • Post-processors are skipped when the handler or a pre-processor throws.