Support global endpoint filters and metadata on WebApplication
- Dominant language
- C#
- Stars
- 38.4k
- Forks
- 10.9k
- Avg merge
- 2d 6h
- Merged PRs (30d)
- 290
Description
## Background and Motivation
In the past, there have been requests to support adding endpoint filters at the global level (see https://github.com/dotnet/aspnetcore/issues/43237). More recently, the need for global filters has emerged as part of the work to support a source generator-based validation implementation in minimal APIs (see https://github.com/dotnet/aspnetcore/issues/46349).
Endpoint filters are implemented on top of ASP.NET Core's conventions API and the `IEndpointConventionBuilder`. To support global filters, we'll need to expose an API for accessing an underlying `IEndpointConventionBuilder` on the top-level `WebApplication`.
## Proposed API
```diff
// Assembly: Microsoft.AspNetCore
namespace Microsoft.AspNetCore.Builder;
public sealed class WebApplication : IHost, IApplicationBuilder, IEndpointRouteBuilder, IAsyncDisposable
{
+ public IEndpointConventionBuilder Conventions { get; }
}
```
## Usage Examples
```csharp
var app = WebApplication.Create();
// Register parameter validations for minimal APIs globally
app.Conventions.WithValidation();
// Disable anti-forgery checks on form-based endpoints during development
if (app.Environment.IsDevelopment())
{
app.Conventions.DisableAntiforgery();
}
// Require authorization on all endpoints in the application
app.Conventions.RequireAuthorization();
app.MapGet("/", () => "Hello world!");
app.Run();
```
## Alternative Designs
Instead of exposing the `IEndpointConventionBuilder` as a property on the `WebApplication` class, we can consider implementing the `IEndpointConventionBuilder` interface directly on the class.
```diff
// Assembly: Microsoft.AspNetCore
namespace Microsoft.AspNetCore.Builder;
public sealed class WebApplication : IHost, IApplicationBuilder, IEndpointRouteBuilder, IAsyncDisposable, IEndpointConventionBuilder
{
+ public void Add(Action convention) { }
+ public void Finally(Action finallyConvention) { }
}
```
However, there's hesitation around having `WebApplication` implement the `IEndpointConventionBuilder` which is an interface with multiple extension methods ([ref](https://source.dot.net/#Microsoft.AspNetCore.Http.Abstractions/Extensions/IEndpointConventionBuilder.cs,8053f043ef3727d0,references)) that might pollute the API surface for the `WebApplication`. Isolating these extension methods to a property keeps the Intellisense on `WebApplication` clean. On the other hand, there are discoverability questions around the `Conventions` property.
Alternatively, we can consider exposing a deconstructor on the `WebApplication` class that allows the user to pull out the underlying `IEndpointConventionBuillder`.
```diff
// Assembly: Microsoft.AspNetCore
namespace Microsoft.AspNetCore.Builder;
public sealed class WebApplication : IHost, IApplicationBuilder, IEndpointRouteBuilder, IAsyncDisposable
{
+ public void Deconstruct(out IEndpointConventionBuilder conventions) { }
}
```
## Risks
- The name `Conventions` comes from an implementation details of the framework and is not as familiar to users as related terms like "filter" and "metadata", which map to conventions under the hood. This might impact the discoverability of this feature.
- Supporting global filters on WebApplication means that users will need to understand the distinction between global middleware and global filters. We'll need to make sure that the distinction is clear to users who might not be familiar with the different pipelines.
- Since conventions are global and applied to all `RouteEndpoints` there is a chance that users can use the API in a way that results in non-sensical permutations. For example, a user can invoke `app.Conventions.WithGroupName("global")` to add a global group name to all endpoints. This concept only makes sense in endpoints that use link-generation or OpenAPI and would be non-sensical of Blazor or SignalR endpoints.
Contributor guide
Assessment
This issue has not been assessed yet.