Hybrid Endpoints

Adding IPipelineBehavior for Hybrid Enpdoints

In hybrid endpoints resolve IRequestDispatcher instead of IRequestHandler.

( ... )
builder.Services
    // Register the IPipelineBehavior<,>
    .AddScoped(typeof(IPipelineBehavior<,>), typeof(ExampleBehavior<,>))
    .MapGet("/hybrid-endpoint", scope => async context =>
    {
        // Does not execute behaviors
        //var requestHandler = scope.GetRequiredService<IRequestHandler<RequestQuery, RequestResult>>();
        //var result = await requestHandler.Handle(new RequestQuery(), context.CancellationToken);

        // Executes behaviors
        var dispatcher = scope.GetRequiredService<IRequestDispatcher<Http11Context>>(); // Use the corresponding IContext
        var result = await dispatcher.Send(new RequestQuery());

        context
            .Respond()
            .Status(ResponseStatus.Ok)
            .Type("application/json")
            .Content(new JsonContent(result, JsonSerializerOptions.Default));
    })
( ... )

// Define the IPipelineBehavior<TRequest, TResponse>
public class ExampleBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> 
    where TRequest : notnull
{
    public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
    {
        // Execute pre endpoint behavior logic here

        // Execute next IPipelineBehavior or IRequestHandler
        return await next();

        // Execute post endpoint behavior logic here
    }
}

Void response

IPipelineBehavior also supports void response just like IRequestHandler

( ... )
builder.Services
    .AddScoped(typeof(IPipelineBehaviorNoResponse<>), typeof(ExampleBehavior<>))
    .MapGet("/hybrid-endpoint", scope => async context =>
    {
        var dispatcher = scope.GetRequiredService<IRequestDispatcher<Http11Context>>();
        await dispatcher.Send(new RequestQuery(), context.CancellationToken);

        context
            .Respond()
            .Status(ResponseStatus.Ok);
    })
( ... )

public class ExampleBehavior<TRequest> : IPipelineBehaviorNoResponse<TRequest>
    where TRequest : IRequest
{
    public async Task Handle(TRequest request, RequestHandlerDelegate next, CancellationToken cancellationToken)
    {
        // Execute pre endpoint behavior logic here
        
        // Execute next IPipelineBehavior or IRequestHandler
        await next();

        // Execute post endpoint behavior logic here
    }
}

public record RequestQuery() : IRequest;

Register a IPipelineBehavior for specific endpoints

In the previous example we are registering a pipeline behavior for any type, in the following example a void response IRequestHandler will be used but this applies for both IRequestHandler<> and IRequestHandler<,>.

.AddScoped(typeof(IPipelineBehavior<,>), typeof(ExampleBehavior<,>)

By defining the TRequest, we can filter which endpoints execute the pipeline behavior.

( ... )
builder.Services
    .AddScoped(typeof(IPipelineBehaviorNoResponse<RequestQuery>), typeof(ExampleBehavior<RequestQuery>))
    .MapGet("/hybrid-endpoint", scope => async context =>
    {
        var dispatcher = scope.GetRequiredService<IRequestDispatcher<Http11Context>>();
        // Sends for RequestQuery TRequest
        await dispatcher.Send(new RequestQuery(), context.CancellationToken);

        context
            .Respond()
            .Status(ResponseStatus.Ok);
    })
( ... )

public class RequestHandlerExample : IRequestHandler<RequestQuery>
{
    public async Task Handle(RequestQuery request, CancellationToken cancellationToken)
    {
        await Task.Delay(0, cancellationToken); // Do work
    }
}
public record RequestQuery() : IRequest;

public class ExampleBehavior<TRequest> : IPipelineBehaviorNoResponse<RequestQuery>
    where TRequest : IRequest
{
    public async Task Handle(RequestQuery request, RequestHandlerDelegate next, CancellationToken cancellationToken)
    {
        // Execute pre endpoint behavior logic here

        await next();

        // Execute post endpoint behavior logic here
    }
}