Blog
Start playing with .NET 7, Minimal APIs and MediatR
Let's start playing with .NET 7, Minimal APIs and MediatR by creating a basic API to see how all these concepts fit together.
First step, we create an empty .NET 7 ASP.NET Core web project.
Now we add MediatR to the project using NuGet.
We create our own interface for the HTTP request, which inherits from the MediatR IRequest interface.
using MediatR;
namespace MinimalApiWithMediatRLab.Requests;
public interface IHttpRequest : IRequest<IResult>
{
}
We add an HTTP request object, let's say we want to add an endpoint to get a customer by their identifier.
namespace MinimalApiWithMediatRLab.Requests;
public class GetCustomerRequest : IHttpRequest
{
public int Id { get; set; }
}
We create a handler to process this request and execute the behavior we want.
using MediatR;
using MinimalApiWithMediatRLab.Requests;
namespace MinimalApiWithMediatRLab.Handlers;
internal class GetCustomerHandler : IRequestHandler<GetCustomerRequest, IResult>
{
public async Task<IResult> Handle(GetCustomerRequest request, CancellationToken cancellationToken)
{
await Task.Delay(10); // To simulate some business logic
return Results.Ok(new
{
Id = request.Id,
FirstName = "Adrien",
LastName = "Torris",
Message = $"Hello customer #{request.Id}"
});
}
}
We create an extension class to configure our MediatR implementation:
using MinimalApiWithMediatRLab.Requests;
using MediatR;
internal static class MediatRExtensions
{
internal static WebApplication MediateGet(this WebApplication app, string template) where TRequest : IHttpRequest
{
app.MapGet(template, async (IMediator mediator, [AsParameters] TRequest request) => await mediator.Send(request));
return app;
}
}
We configure our application to use MediatR:
builder.Services.AddMediatR(x => x.AsScoped(), typeof(Program));
Next we declare the endpoint we want to add:
app.MediateGet("customer/{id}");
Now we can run our application and call the endpoint we created:
We now have an API that works but doesn't do much. We have only one GET method, let's implement another HTTP verb, like a POST method. To handle other verbs, you just have to map them in our extension class:
using MinimalApiWithMediatRLab.Requests;
using MediatR;
internal static class MediatRExtensions
{
internal static WebApplication MediateGet<TRequest>(this WebApplication app, string template) where TRequest : IHttpRequest
{
app.MapGet(template, async (IMediator mediator, [AsParameters] TRequest request) => await mediator.Send(request));
return app;
}
internal static WebApplication MediatePost<TRequest>(this WebApplication app, string template) where TRequest : IHttpRequest
{
app.MapPost(template, async (IMediator mediator, [AsParameters] TRequest request) => await mediator.Send(request));
return app;
}
}
Now we can add a POST endpoint by doing exactly the same thing as for the GET ones, but by using the MediatePost method.
Also, to be closer to a real-world scenario where there is often business logic involved, let's add a service and inject it into the handler. To continue with our customer example, let's create a Customer service with a Get method:
public class Customer
{
public int Id { get; init; }
public string FirstName { get; init; }
public string LastName { get; init; }
public Customer(int id, string firstName, string lastName)
{
this.Id = id;
this.FirstName = firstName;
this.LastName = lastName;
}
}
namespace MinimalApiWithMediatRLab.Services;
public class CustomerService : ICustomerService
{
private static readonly Customer[] customers =
{
new Customer(1, "Adrien", "Torris"),
new Customer(2, "Scott", "Parker"),
new Customer(3, "Bruce", "Wayne"),
new Customer(4, "Tin", "Tin")
};
public async Task<Customer> GetAsync(int id)
{
await Task.Delay(10);
return customers.SingleOrDefault(x => x.Id == id);
}
}
Once our service is created, we must register it in the Dependency Injection container to make it available to the consumers that want to use it:
builder.Services.AddSingleton();
Now that our service is ready for use, we inject the CustomerService in our HTTP request handler using the built-in dependency injection framework and use it in our Handle method:
using MediatR;
using MinimalApiWithMediatRLab.Requests;
using MinimalApiWithMediatRLab.Services;
namespace MinimalApiWithMediatRLab.Handlers;
internal class GetCustomerHandler : IRequestHandler<GetCustomerRequest, IResult>
{
private readonly ICustomerService _customerService;
public GetCustomerHandler(ICustomerService customerService)
{
_customerService = customerService;
}
public async Task Handle(GetCustomerRequest request, CancellationToken cancellationToken)
{
Customer customer = await _customerService.GetAsync(request.Id);
return customer != null
? Results.Ok(customer)
: Results.NotFound();
}
}
The code sample of this minimal project is available on my GitHub.