An open API service indexing awesome lists of open source software.

https://github.com/simcubeltd/simcube.spartan

.Net 7 minimal apis with Mediatr, FluentValidation and usage of AsParametersAttribute for request handling
https://github.com/simcubeltd/simcube.spartan

dotnet minimal-api

Last synced: 9 months ago
JSON representation

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

Awesome Lists containing this project

README

          

# Spartan

Testable Clean Minimal Apis, with Mediatr and Fluent Validation.

Including Endpoint configuration, and IEndpointFilters!

Utilising .Net 7's  [AsParametersAttribute](https://docs.microsoft.com/dotnet/api/microsoft.aspnetcore.http.asparametersattribute?view=aspnetcore-7.0)

 

[![codecov](https://codecov.io/gh/SimCubeLtd/SimCube.Spartan/branch/main/graph/badge.svg?token=YW1VVMY0UK)](https://codecov.io/gh/SimCubeLtd/SimCube.Spartan) [![main](https://github.com/SimCubeLtd/SimCube.Spartan/actions/workflows/main.yml/badge.svg)](https://github.com/SimCubeLtd/SimCube.Spartan/actions/workflows/main.yml) ![Nuget](https://img.shields.io/nuget/v/SimCube.Spartan?style=flat-square)

## [Documentation](./docs/README.md)

## Usage/Examples
### See Examples of registration in the [DemoRegistrationMethods](./src/SimCube.Spartan.ExampleConsole/DemoRegistrationMethods.cs) class.

### Using Attributes

#### Application Startup

```csharp
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

```csharp
[MediatedRequest(RequestType.MediatedGet, "example/{name}/{age}")]
public record GetExampleRequest(int Age, string Name) : IMediatedRequest;

public class GetExampleRequestValidator : AbstractValidator
{
public GetExampleRequestValidator() =>
RuleFor(x => x.Age)
.GreaterThan(18)
.WithMessage("You must be 18 to use this service.");
}

public class GetExampleRequestHandler : IRequestHandler
{
public Task 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.

```csharp
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("example/{name}/{age}", routeHandlerBuilder => routeHandlerBuilder.WithName("GetExample"));
app.MediatedPatch("example/{name}/{age}");

app.Run();
```

#### Request, Optional Validation and Handler

```csharp
public record GetExampleRequest(int Age, string Name) : IMediatedRequest;

public class GetExampleRequestValidator : AbstractValidator
{
public GetExampleRequestValidator() =>
RuleFor(x => x.Age)
.GreaterThan(18)
.WithMessage("You must be 18 to use this service.");
}

public class GetExampleRequestHandler : IRequestHandler
{
public Task 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!

```csharp
[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 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 ConfigureEndpoint() => builder =>
builder.AllowAnonymous()
.WithName("DeleteStuff");
}
```

### Stream Support

#### Mediatr streams are also supported, which produce IAsyncEnumerables.

With attribute, also with endpoint route builder handler.

```csharp
[MediatedEndpoint(RequestType.MediatedGet, "getstream")]
public class GetStreamExampleRequest : BaseMediatedStream
{
///
public override Action ConfigureEndpoint() =>
builder => builder.AllowAnonymous();
}
```

As record
```csharp
public record GetStreamExampleRequestTwo : IStreamRequest;
```

#### Example Handler
```csharp
using System.Runtime.CompilerServices;
using Bogus;
using Person = SimCube.Spartan.ExampleConsole.Models.Person;

namespace SimCube.Spartan.ExampleConsole.Handlers;

///
/// Th example request handler.
///
public class GetStreamExampleRequestHandler : IStreamRequestHandler
{
private readonly Faker _faker;

///
/// Initializes a new instance of the class.
///
public GetStreamExampleRequestHandler()
{
Randomizer.Seed = new(1701);

_faker = new Faker()
.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);
}

///
public async IAsyncEnumerable Handle(
GetStreamExampleRequest request,
[EnumeratorCancellation] CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
await Task.Delay(200, cancellationToken);

yield return _faker.Generate();
}
}
}

```

## Acknowledgements

- [Mediatr](https://github.com/jbogard/MediatR)
- [FluentValidation](https://docs.fluentvalidation.net/en/latest/)
- [MIcrosoft](https://dotnet.microsoft.com/en-us/download/dotnet/7.0)
- [Nick Chapsas](https://www.youtube.com/c/Elfocrash)

## Authors

- [@prom3theu5](https://www.github.com/prom3theu5)