With ASP.NET Core 7, we can take advantage of the newly introduced IEndpointFilter interface to create filters and attach them to the endpoints in our minimal APIs. These filters can be used to modify request or response objects or to short-circuit the request processing pipeline.
This article discusses how we can work with endpoint filters when building minimal API applications in ASP.NET Core 7. To use the code examples provided in this article, you should have Visual Studio 2022 installed in your system. If you don’t already have a copy, you can download Visual Studio 2022 here.
Create an ASP.NET Core 7 minimal Web API project in Visual Studio 2022
First off, let’s create an ASP.NET Core 7 project in Visual Studio 2022 Preview. Follow these steps:
- Launch the Visual Studio 2022 IDE.
- Click on “Create new project.”
- In the “Create new project” window, select “ASP.NET Core Web API” from the list of templates displayed.
- Click Next.
- In the “Configure your new project” window, specify the name and location for the new project.
- Optionally check the “Place solution and project in the same directory” check box, depending on your preferences.
- Click Next.
- In the “Additional Information” window shown next, select “NET 7.0 (Current)” as the framework and uncheck the check box that says “Use controllers…” since we’ll be using minimal APIs in this example. Leave “Authentication Type” set to “None” (default).
- Ensure that the check boxes “Enable OpenAPI Support,” “Enable Docker”, “Configure for HTTPS,” and “Enable Open API Support” are unchecked as we won’t be using any of those features here.
- Click Create.
We’ll use this ASP.NET Core 7 Web API project to create a minimal API and implement endpoint filters in the sections below.
What is an endpoint filter?
A filter is code that can be run at different points during the request processing pipeline, before or after an action. You could use a filter to check for authorization, or to check request parameters, or to record each time a web page is accessed, for example.
An endpoint filter can be invoked on actions and on route endpoints. An endpoint is a URL that is the entry point for an application, such as http://example.com/.
Key benefits of filters include enhanced security and simplify your code base through code reuse. For example, you can use filters to:
- Reject requests that don’t meet specific criteria.
- Create reusable functions and classes.
- Focus on the business logic of your application instead of spending time writing code for cross-cutting concerns.
- Execute code before and after an endpoint handler.
- Log request and response metadata.
- Validate a request and request parameters.
An endpoint filter enables you to intercept an incoming request, alter it, short-circuit it, and even consolidate your cross-cutting concerns in a single place. Typical examples of cross-cutting concerns that could be handled in a single class are authorization, validation, and exception handling.
The IEndpointFilter interface in ASP.NET Core 7
ASP.NET Core 7 introduces a new interface named IEndpointFilter that can be used to modify the request, modify the response, or short-circuit the request pipeline. The IEndpointFilter interface can be used to add information to incoming requests before the endpoint processes them.
The IEndpointFilter interface is defined in the Microsoft.AspNetCore.Http namespace as shown below.
public interface IEndpointFilter
{
ValueTask<object?> InvokeAsync(
EndpointFilterInvocationContext context,
EndpointFilterDelegate next);
}
Why should I use the IEndpointFilter interface?
The IEndpointFilter interface is used to add functionality to an HTTP endpoint. It is a simple interface with only one method, called InvokeAsync, that can add custom logic to the request/response pipeline.
You can use the IEndpointFilter interface when you want to modify the request or response at a specific point in the pipeline. You can use it to put all of your cross-cutting components, such as logging, authentication, authorization, and encryption, in one place, where you can maintain them without altering any other part of your application’s code that leverages these components.
Create an endpoint filter in ASP.NET Core 7
You can register a filter by using a delegate that accepts EndPointFilterInvocationContext as a parameter and returns an EndpointFilterDelegate. The EndPointFilterInvocationContext instance provides access to the HttpContext of the current request.
The following code snippet shows how you can create a simple endpoint filter that returns some text. Note how HttpContext has been used to retrieve the Request headers.
string AuthorName(string author) => $"Name of author: {author}";
app.MapGet("/endpointfilterexample/{author}", AuthorName)
.AddEndpointFilter(async (invocationContext, next) =>
{
var httpContext = invocationContext.HttpContext;
var requestHeaders = httpContext.Request.Headers;
var author = invocationContext.GetArgument<string>(0);
return await next(invocationContext);
});
When you execute the preceeding piece of code, the request headers will be displayed as shown in Figure 1 below.
Chain multiple endpoint filters together in ASP.NET Core 7
The following code demonstrates how multiple endpoint filters can be chained together in the default endpoint.
app.MapGet("/", () =>
{
return "Demonstrating multiple filters chained together.";
})
.AddEndpointFilter(async (endpointFilterInvocationContext, next) =>
{
app.Logger.LogInformation("This is the first filter.");
var result = await next(endpointFilterInvocationContext);
return result;
})
.AddEndpointFilter(async (endpointFilterInvocationContext, next) =>
{
app.Logger.LogInformation("This is the second filter.");
var result = await next(endpointFilterInvocationContext);
return result;
})
.AddEndpointFilter(async (endpointFilterInvocationContext, next) =>
{
app.Logger.LogInformation("This is the third context.");
var result = await next(endpointFilterInvocationContext);
return result;
});
When you execute the endpoint, the three endpoint filters will be executed one after another.
Create a custom filter in a minimal API in ASP.NET Core 7
You can also create custom filters by implementing the IEndpointFilter interface as shown in the code snippet given below.
public class MyCustomFilter : IEndpointFilter
{
public async ValueTask<object?> InvokeAsync(
EndpointFilterInvocationContext context,
EndpointFilterDelegate next
)
{
if (context.HttpContext.GetRouteValue("city") is string city)
{
return Results.Ok($"The name of the city is: {city}");
}
return await next(context);
}
}
app.MapGet("/demo/{city}", () =>
{
return "Execute filters in a chain.";
})
.AddEndpointFilter<MyCustomFilter>();
To access the parameters associated with a particular HTTP request, you can use the GetArguments method of the EndpointFilterInvocationContext object. If you have multiple filters, you can register them using the following code in your Program.cs file.
app.MapGet("/", () =>
{
return "This is a sample text.";
})
.AddEndpointFilter<MyCustomFilterA>()
.AddEndpointFilter<MyCustomFilterB>()
.AddEndpointFilter<MyCustomFilterC>();
Filters in ASP.NET Core allow you to run custom code before or after a certain point in the request processing pipeline. You can take advantage of endpoint filters to short-circuit endpoint executions or implement validation logic in your minimal API. I’ll discuss endpoint filters in more detail in a future post here.