DEV Community

Cover image for .Net 7 (Preview 4) - Minimal API Filters
Mohamad Lawand
Mohamad Lawand

Posted on

.Net 7 (Preview 4) - Minimal API Filters

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
Enter fullscreen mode Exit fullscreen mode

Get the repository

https://github.com/mohamadlawand087/MinimalApi-JWT
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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;
    }
}
Enter fullscreen mode Exit fullscreen mode

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>>();
Enter fullscreen mode Exit fullscreen mode

Please ask any questions or clarification you might need.

Discussion (0)