Skip to content

Global endpoint filter (edit: and other conventions?) #43237

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
1 task done
WeihanLi opened this issue Aug 12, 2022 · 14 comments
Open
1 task done

Global endpoint filter (edit: and other conventions?) #43237

WeihanLi opened this issue Aug 12, 2022 · 14 comments
Assignees
Labels
api-needs-work API needs work before it is approved, it is NOT ready for implementation area-minimal Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc
Milestone

Comments

@WeihanLi
Copy link
Contributor

WeihanLi commented Aug 12, 2022

Is there an existing issue for this?

  • I have searched the existing issues

Is your feature request related to a problem? Please describe the problem.

Since we introduced the endpoint filter feature, is there also a global endpoint filter available?

So that we could avoid registering the endpoint filter for each endpoint.

Describe the solution you'd like

Maybe likes MVC global filters, and maybe group endpoint filter for a Group (MapGroup) likes the filter for controller

Additional context

No response

@javiercn javiercn added the old-area-web-frameworks-do-not-use *DEPRECATED* This label is deprecated in favor of the area-mvc and area-minimal labels label Aug 12, 2022
@davidfowl
Copy link
Member

davidfowl commented Aug 14, 2022

The way to do this is with groups. Make a global group and stick all of the endpoints in it. In the future, we may consider a default convention builder on the WebApplication object.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

var routes = app.MapGroup("");

routes.AddEndpointFilter((context, next) =>
{
    return next(context);
});

routes.MapGet("/", () => "Hello World!");

app.Run();

@WeihanLi
Copy link
Contributor Author

Thanks @davidfowl

@halter73
Copy link
Member

halter73 commented Aug 16, 2022

I love using an empty group prefix for this! Support for gobal metadata and filters was the main reason I figured it was worth allowing empty group prefixes. I think we can do better though. What do we think about having WebApplication implement IEndpointConventionBuilder.

Background and Motivation

See above.

Proposed API

namespace Microsoft.AspNetCore.Builder;

namespace Microsoft.AspNetCore.Http;

- public sealed class WebApplication : IHost, IApplicationBuilder, IEndpointRouteBuilder, IAsyncDisposable
+ public sealed class WebApplication : IHost, IApplicationBuilder, IEndpointRouteBuilder, IEndpointConventionBuilder, IAsyncDisposable

Usage Examples

var app = WebApplication.Create(args);

app.AddEndpointFilter((context, next) =>
{
    return next(context);
});

app.MapGet("/", () => "Hello World!");

app.Run();

Alternative Designs

Leave it as is. You can always create an route group with an empty prefix if you want this functionality as @davidfowl suggests:

var app = WebApplication.Create(args);
var routes = app.MapGroup("");

routes.AddEndpointFilter((context, next) =>
{
    return next(context);
});

routes.MapGet("/", () => "Hello World!");

app.Run();

Risks

It would be too noisy to have too many extension methods that all work on WebApplication.

@halter73 halter73 added api-suggestion Early API idea and discussion, it is NOT ready for implementation api-ready-for-review API is ready for formal API review - https://github.com/dotnet/apireviews and removed api-suggestion Early API idea and discussion, it is NOT ready for implementation labels Aug 16, 2022
@halter73 halter73 added this to the .NET 8 Planning milestone Aug 16, 2022
@ghost
Copy link

ghost commented Aug 16, 2022

Thank you for submitting this for API review. This will be reviewed by @dotnet/aspnet-api-review at the next meeting of the ASP.NET Core API Review group. Please ensure you take a look at the API review process documentation and ensure that:

  • The PR contains changes to the reference-assembly that describe the API change. Or, you have included a snippet of reference-assembly-style code that illustrates the API change.
  • The PR describes the impact to users, both positive (useful new APIs) and negative (breaking changes).
  • Someone is assigned to "champion" this change in the meeting, and they understand the impact and design of the change.

@halter73 halter73 changed the title Global endpoint filter Global endpoint filter (edit: and other conventions?) Aug 16, 2022
@WeihanLi
Copy link
Contributor Author

The MatchGroup with empty had to update the existing code, if there are a lot of endpoints it may need a big effort to update.
Personally, I prefer the proposal

@davidfowl
Copy link
Member

davidfowl commented Aug 16, 2022

I don't like this proposal I think top level intellisense will be too noisy. Routing methods are usually Map*, middleware methods are Use*, these conventions could don't have a consistent naming scheme. We use With*, Add*, Require*. I'm worried about jumbled intellisense and naming conflicts.

@davidfowl
Copy link
Member

davidfowl commented Aug 17, 2022

Here's an alternative proposal:

namespace Microsoft.AspNetCore.Http;

public sealed class WebApplication 
{
+    public RouteGroupBuilder Routes { get; }
}

We expose a default top level RouteGroup that you can hang your routes on. Internally, we'd create a default route group and would use it for both top level routes hanging off the WebApplication itself and for the Routes property.

app.Routes.MapGet("/", () => "Hello");

is the same as:

app.MapGet("/", () => "Hello");

Adding global conventions is then:

app.Routes.AddEndpointFilter((context, next) =>
{
    return next(context);
});

It also solves some of the complaints we've had in this area:

@halter73
Copy link
Member

Another alternative is adding a Conventions property that's just an IEndpointConventionBuilder. Under the covers, we would use groups to implement this, but we don't need to expose that implementation detail if we don't want to.

namespace Microsoft.AspNetCore.Http;

public sealed class WebApplication 
{
+    public IEndpointConventionBuilder Conventions { get; }
}

Usage:

app.MapGet("/", () => "Hello");

app.Conventions.AddEndpointFilter((context, next) =>
{
    return next(context);
});

We've said that we don't want APIs targeting RouteGroupBuilder directly. It's a sealed type. Any single API should target either IEndpointRouteBuilder or IEndpointConventionBuilder, but not both. If later on, we feel this is a mistake, we could create a new interface that implements both. We could then have RouteGroupBuilder implement that and tell people to target the new combined interface, but I don't think we're going to need that.

@halter73
Copy link
Member

Or we could do a hybrid approach, so we still allow you to delineate between the IApplicationBuilder and the IEndpointConventionBuilder if you want to.

namespace Microsoft.AspNetCore.Http;

public sealed class WebApplication 
{
+    public IEndpointRouteBuilder Routes { get; }
+    public IEndpointConventionBuilder Conventions { get; }
}

Usage:

app.Routes.MapGet("/", () => "Hello");

app.Conventions.AddEndpointFilter((context, next) =>
{
    return next(context);
});

@davidfowl
Copy link
Member

I considered this as well but I figured 2 birds with one stone (see the blog posts). Is there any reason why we can't expose a sealed type? Are you predicting extension methods targeting this?

@halter73
Copy link
Member

I considered this as well but I figured 2 birds with one stone (see the blog posts).

I just read them before I suggested the hybrid proposal that addresses the concern of both blogposts. I think it's better because it's not adding yet a new property that arguably implements too many interfaces on a single type.

Are you predicting extension methods targeting this?

I hope not. If we were willing to make a major change to the route group APIs in the 11th hour of .NET 7 we could change RouteGroupBuilder itself to not implement two very distinct interfaces. Arguably, this would avoid repeating the mistake we made with WebApplication:

namespace Microsoft.AspNetCore.Routing;

- public sealed class RouteGroupBuilder : IEndpointRouteBuilder, IEndpointConventionBuilder
+ public sealed class RouteGroupBuilder
{
+     public IEndpointRouteBuilder Routes { get; }
+     public IEndpointConventionBuilder Conventions { get; }
}

With this API change, the current workaround for global convetions goes from being like:

var app = WebApplication.Create(args);
var routes = app.MapGroup("");

routes.AddEndpointFilter((context, next) =>
{
    return next(context);
});

routes.MapGet("/", () => "Hello World!");

app.Run();

To something like this:

var app = WebApplication.Create(args);
var group = app.MapGroup("");

group.Conventions.AddEndpointFilter((context, next) =>
{
    return next(context);
});

group.Routes.MapGet("/", () => "Hello World!");

app.Run();

@andrewlock and @khalidabuhakmeh do either of you have any thoughts on this?

@halter73 halter73 added api-needs-work API needs work before it is approved, it is NOT ready for implementation and removed api-ready-for-review API is ready for formal API review - https://github.com/dotnet/apireviews labels Aug 22, 2022
@halter73
Copy link
Member

halter73 commented Aug 22, 2022

API Review Notes:

  • Having to type the extra .Routes or .Conventions characters is annoying.
  • Can we use destructors?
    • This enables you to delineate between the IApplicationBuilder and the IEndpointConventionBuilder if you want to.
  • Can we use it for RouteGroupBuilder?
  • Can we add an empty overload to MapGroup without the prefix parameter? If this is the preferred way to add conventions to a group of endpoints, it might make sense.

We feel we're going to need customer feedback to see if we really want to do any of the above proposals.

We do not think we want RouteGroupBuilder to stop implementing both IEndpointRouteBuilder and IEndpointConventionBuilder. That's one of the few decisions we won't be able to take back after .NET 7.

Most of the other suggestions are additive and could be done in a later release.

@andrewlock
Copy link
Contributor

do either of you have any thoughts on this?

I mean, I have definite thoughts if we could turn back time and have WebApplication not directly implement IEndpointRouteBuilder (as I mentioned in the blog post), but I assume that's not really on the cards 😄

For the hybrid approach:
I think having WebApplication directly implement IEndpointRouteBuilder and having a Routes property may be worse than one or the other, as it adds confusion/ambiguity (i.e. the difference between app.MapGet() and app.Routes.MapGet() is not obvious)

RE splitting RouteBuilder:
I'm not sure if it adds a lot splitting IEndpointRouteBuilder and IEndpointConventionBuilder other than make things a bit more verbose. I quite like that I didn't even appreciate that there were two interfaces involved here previously, as everything just flows quite nicely.

For me the top-level Conventions property makes most sense probably, though I'm not a fan of the name (I don't think of filters etc as being "conventions", and nor do any of the docs, even if that's now they're implemented).

Personally, I think the original workaround, of creating a "top-level route" is the best option given the existing constraints

@khalidabuhakmeh
Copy link

I agree with @andrewlock in the sense that Conventions doesn't make a lot of sense here. I would consider it as part of the request pipeline, so possibly Pipeline or Filters (to stay in line with Razor Pages and MVC) would make the most sense.

The thing I like about ASP.NET Core is the shared model, and that includes the shared mental model across all the approaches. Trying to be too different might drive people to "pick sides", rather than realizing the strength is in flowing between Razor Pages, Minimal API endpoints, MVC, and SignalR.

@captainsafia captainsafia added area-minimal Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc and removed old-area-web-frameworks-do-not-use *DEPRECATED* This label is deprecated in favor of the area-mvc and area-minimal labels labels Jun 20, 2023
@captainsafia captainsafia modified the milestones: .NET 8 Planning, Backlog Mar 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api-needs-work API needs work before it is approved, it is NOT ready for implementation area-minimal Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc
Projects
No open projects
Status: No status
Development

No branches or pull requests

7 participants