Testable Clean Minimal Apis, with Mediatr and Fluent Validation.
Including Endpoint configuration, and IEndpointFilters!
Utilising .Net 7's  AsParametersAttribute
Â
See Examples of registration in the DemoRegistrationMethods class.
builder.Services.AddSpartanInfrastructure(x => x.AsScoped());
// Or pass through assemblies to scan for handlers
// builder.Services.AddSpartanInfrastructure(x => x.AsScoped(), typeof(MyAssemblyOne), typeof(MyAssemblyTwo));
[MediatedRequest(RequestType.MediatedGet, "example/{name}/{age}")]
public record GetExampleRequest(int Age, string Name) : IMediatedRequest;
public class GetExampleRequestValidator : AbstractValidator<GetExampleRequest>
{
public GetExampleRequestValidator() =>
RuleFor(x => x.Age)
.GreaterThan(18)
.WithMessage("You must be 18 to use this service.");
}
public class GetExampleRequestHandler : IRequestHandler<GetExampleRequest, IResult>
{
public Task<IResult> Handle(GetExampleRequest request, CancellationToken cancellationToken) =>
Task.FromResult(Results.Ok($"The age was {request.Age} and the name was {request.Name}"));
}
You can enable or disable registration of FluentValidation on the AddSpartanInfrastructure extension method.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSpartanInfrastructure(x => x.AsScoped());
// Or pass through assemblies to scan for handlers
// builder.Services.AddSpartanInfrastructure(x => x.AsScoped(), typeof(MyAssemblyOne), typeof(MyAssemblyTwo));
// You can also disable FluentValidation by registering with:
//builder.Services.AddSpartanInfrastructure(x => x.AsScoped(), false);
var app = builder.Build();
app.AddMediatedEndpointsFromAttributes();
app.MediatedGet<GetExampleRequest>("example/{name}/{age}", routeHandlerBuilder => routeHandlerBuilder.WithName("GetExample"));
app.MediatedPatch<PatchExampleRequest>("example/{name}/{age}");
app.Run();
public record GetExampleRequest(int Age, string Name) : IMediatedRequest;
public class GetExampleRequestValidator : AbstractValidator<GetExampleRequest>
{
public GetExampleRequestValidator() =>
RuleFor(x => x.Age)
.GreaterThan(18)
.WithMessage("You must be 18 to use this service.");
}
public class GetExampleRequestHandler : IRequestHandler<GetExampleRequest, IResult>
{
public Task<IResult> Handle(GetExampleRequest request, CancellationToken cancellationToken) =>
Task.FromResult(Results.Ok($"The age was {request.Age} and the name was {request.Name}"));
}
Requests can also derive from the BaseMediatedRequest class, and override the ConfigureEndpoint method to chain route endpoint configuration such as Cache, WithName, Produces etc.
You also have the ability to supply a collection of IEndpointFilters to chain to the endpoint, by overriding the 'EndpointFilters' property!
[MediatedRequest(RequestType.MediatedDelete, "example/{name}/{age}")]
public class DeleteExampleRequest : BaseMediatedRequest
{
public DeleteExampleRequest(int age, string name)
{
Age = age;
Name = name;
}
public int Age { get; }
public string Name { get; }
// Here we override the EndpointFilters property, chaining the filter onto the request endpoint
// The are processed in the order that they appear in the list.
public override List<IEndpointFilter> EndpointFilters => new()
{
new ExampleNameIsPrometheusFilter()
};
// Here we override the invocation of the configuration of the route handler builder,
// Allowing you to add CacheOutput, manual filters, WithName, Produces etc.
public override Action<RouteHandlerBuilder> ConfigureEndpoint() => builder =>
builder.AllowAnonymous()
.WithName("DeleteStuff");
}
With attribute, also with endpoint route builder handler.
[MediatedEndpoint(RequestType.MediatedGet, "getstream")]
public class GetStreamExampleRequest : BaseMediatedStream<Person>
{
/// <inheritdoc />
public override Action<RouteHandlerBuilder> ConfigureEndpoint() =>
builder => builder.AllowAnonymous();
}
As record
public record GetStreamExampleRequestTwo : IStreamRequest<Person>;
using System.Runtime.CompilerServices;
using Bogus;
using Person = SimCube.Spartan.ExampleConsole.Models.Person;
namespace SimCube.Spartan.ExampleConsole.Handlers;
/// <summary>
/// Th example request handler.
/// </summary>
public class GetStreamExampleRequestHandler : IStreamRequestHandler<GetStreamExampleRequest, Person>
{
private readonly Faker<Person> _faker;
/// <summary>
/// Initializes a new instance of the <see cref="GetStreamExampleRequestHandler"/> class.
/// </summary>
public GetStreamExampleRequestHandler()
{
Randomizer.Seed = new(1701);
_faker = new Faker<Person>()
.StrictMode(true)
.RuleFor(o => o.Name, f => f.Person.FullName)
.RuleFor(o => o.Age, f => f.Random.Number(18, 100))
.RuleFor(o => o.EmailAddress, f => f.Person.Email);
}
/// <inheritdoc />
public async IAsyncEnumerable<Person> Handle(
GetStreamExampleRequest request,
[EnumeratorCancellation] CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
await Task.Delay(200, cancellationToken);
yield return _faker.Generate();
}
}
}