Skip to content

.Net 7 minimal apis with Mediatr, FluentValidation and usage of AsParametersAttribute for request handling

License

Notifications You must be signed in to change notification settings

SimCubeLtd/SimCube.Spartan

Repository files navigation

Spartan

Testable Clean Minimal Apis, with Mediatr and Fluent Validation.

Including Endpoint configuration, and IEndpointFilters!

Utilising .Net 7's  AsParametersAttribute

 

codecov main Nuget

Usage/Examples

See Examples of registration in the DemoRegistrationMethods class.

Using Attributes

Application Startup

builder.Services.AddSpartanInfrastructure(x => x.AsScoped());
// Or pass through assemblies to scan for handlers
// builder.Services.AddSpartanInfrastructure(x => x.AsScoped(), typeof(MyAssemblyOne), typeof(MyAssemblyTwo));

Request, Optional Validation and Handler

[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}"));
}

Directly Using Extensions

Application Startup

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();

Request, Optional Validation and Handler

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");
}

Stream Support

Mediatr streams are also supported, which produce IAsyncEnumerables.

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>;

Example Handler

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();
        }
    }
}

Acknowledgements

Authors

About

.Net 7 minimal apis with Mediatr, FluentValidation and usage of AsParametersAttribute for request handling

Topics

Resources

License

Stars

Watchers

Forks

Languages