.Net 7 (Preview 4) - Minimal API Filters
Tags: dotnet, tutorial, api, programming
Published: 2022-05-20 13:50:30

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 
{% embed https://youtu.be/iA_pot-fkIw %}

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

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

```bash
dotnet run
```

Let us now implement filter to our post method inside our program.cs

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

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

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