In this article we will implement one of the new features introduced with .Net 7 Preview 4 which is Filtering with Minimal API
You can watch the full video on YouTube
To start we will need our repository which is a Minimal API which we created in .NET 6
Get .NET 7 Preview SDK
https://dotnet.microsoft.com/en-us/download/dotnet/7.0
Get the repository
https://github.com/mohamadlawand087/MinimalApi-JWT
The first thing we need to do is update our application to .Net 7 Preview 4, we open our TodoApi.csproj and replace it with the following
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>preview</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.0-preview.4.22229.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.0-preview.4.22229.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.0-preview.4.22229.2" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
</ItemGroup>
</Project>
We can see from the updated code that we have utilised the .Net 7 framework as well enabled C# 11 feature by using the lang version preview and we have updated all of the nuget packages to the latest version
Once that is done, lets run our application to make sure everything is running as it should
dotnet run
Let us now implement filter to our post method inside our program.cs
app.MapPost("/items", [Authorize] async (ApiDbContext db, Item item) => {
if( await db.Items.FirstOrDefaultAsync(x => x.Id == item.Id) != null)
{
return Results.BadRequest();
}
db.Items.Add(item);
await db.SaveChangesAsync();
return Results.Created( $"/Items/{item.Id}",item);
}).AddFilter((ctx, next) => async (context) =>
{
return CheckIncomingObj(context) == false ? Results.BadRequest("Invalid parameters") : await next(context);
});
bool? CheckIncomingObj(RouteHandlerInvocationContext context)
{
if (context.Parameters[1] == null)
return false;
if (context.Parameters[1] is not Item param)
return false;
return IsValidItem(param);
}
bool IsValidItem(Item item)
{
if (item.Title.Length > 1 && item.Id > 0)
return true;
return false;
}
Now lets make these validation more generic
Let us create a new folder called Filters in the root directory and inside the folder will create a new class class ValidationFilters.cs
namespace TodoApi.Filters;
public class ValidationFilter<T> : IRouteHandlerFilter where T : class
{
public async ValueTask<object?> InvokeAsync(RouteHandlerInvocationContext context, RouteHandlerFilterDelegate next)
{
var param = context.Parameters.FirstOrDefault(x => x?.GetType() == typeof(T));
if(param is null)
{
return Results.BadRequest();
}
if (param is Item)
{
var validationResult = IsValidItem(param as Item);
if(!validationResult)
{
return Results.BadRequest("Invalid parameters.");
}
}
// before the endpoint call
var result = await next(context);
// after endpoint call
return result;
}
bool IsValidItem(Item? item)
{
if (item == null)
return false;
if (item.Title.Length > 1 && item.Id > 0)
return true;
return false;
}
}
And now let us update our post method in the program.cs
app.MapPost("/items", [Authorize] async (ApiDbContext db, Item item) =>
{
if (await db.Items.FirstOrDefaultAsync(x => x.Id == item.Id) != null)
{
return Results.BadRequest();
}
db.Items.Add(item);
await db.SaveChangesAsync();
return Results.Created($"/Items/{item.Id}", item);
}).AddFilter<ValidationFilter<Item>>();
Please ask any questions or clarification you might need.
Top comments (0)