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
- Host: GitHub
- URL: https://github.com/simcubeltd/simcube.spartan
- Owner: SimCubeLtd
- License: other
- Created: 2022-08-09T13:58:10.000Z (over 3 years ago)
- Default Branch: main
- Last Pushed: 2025-04-08T22:15:27.000Z (9 months ago)
- Last Synced: 2025-04-08T23:23:21.423Z (9 months ago)
- Topics: dotnet, minimal-api
- Language: C#
- Homepage:
- Size: 152 KB
- Stars: 8
- Watchers: 2
- Forks: 0
- Open Issues: 11
-
Metadata Files:
- Readme: README.md
- License: LICENSE.md
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)
[](https://codecov.io/gh/SimCubeLtd/SimCube.Spartan) [](https://github.com/SimCubeLtd/SimCube.Spartan/actions/workflows/main.yml) 
## [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)