dotnet / dotnet/aspnetcore

Support global endpoint filters and metadata on WebApplication

Open
#59,755 4 comments 1 reaction 0 assignees View on GitHub
api-approved area-minimal
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.