-
Notifications
You must be signed in to change notification settings - Fork 10k
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
Add support for endpoint filters #37853
Comments
We met last week to review a design for this last week. The text of the design is below. There's some open questions that we need to follow-up on but for now we are moving forward with implementation primarily in two phases:
Endpoint Filters DesignDate: January 27, 2022 SummaryFilters are MVC construct that allow developers to. Unlike middlewares, filters run after the routing middleware and have access to additional data that middlewares don’t. While middlewares operate on the Filters, and particularly
Goals
Non-goals
Risks/Unknowns
Proposed Design Walkthrough
public class UnsupportedContentTypeFilter : IEndpointFilter
{
public override ValueTask<object> RunAsync(IEndpointFilterContext context, Func<IEndpointFilterContext, ValueTask<object?>> next)
{
// Assume last argument is some Boolean flag
if (context.Parameters.Last())
{
var result = await next(context);
if (result.ContentType != "application/json")
{
return new UnsupportedMediaTypeResult();
}
return result;
}
}
}
// Option 1
app.MapGet("/todos/{id}", (int id) => new Todo(id, $"Task {id}"))
.AddFilter<UnsupportedContentTypeFilter>();
// Option 2
app
.AddFilter<UnsupportedContentTypeFilter>()
.MapGet("/todos/{id}", (int id) => new Todo(id, $"Task {id}"));
// Option 2.5
app.MapGet("/root", () => "Hello from root").AddFilter<UnsupportedContentTypeFilter>()
var rootGroup1 = app.RouteGroup().AddFilter<UnsupportedContentTypeFilter>;
// Option 3
app
.MapGet(new UnsupportedContentTypeFilter(), "/todos/{id}", (int id) => new Todo(id, $"Task {id}"));
$ http POST http://localhost:5000/todos/1
|
Some feedback
|
A few thoughts... What's the purpose of defining all of Defining Another thing that feels important based on MVC's scenarios is the ability to introspect the method being called and its parameter declarations. This comes up a lot when someone writes a generic reusable filter and distributes it or applies it globally. It's a good way to "build your own DSL" with attributes that can go on methods/parameters. The ability to modify/decorate the result appears to be missing from this. Many results in MVC are mutable for this reason, not sure if that's true of minimal APIs. I'm glad that this design only defines one kind of filter, and it's the most useful kind. Legacy MVC had too many kinds of specialized filters because it was designed for the pre-middleware world. We had to keep these in MVC core for compat reasons and that's the root cause of much of MVC's perf overhead. One of the valid cases that might come up is the "I want to run a filter before model binding occurs". This would require another kind of filter to implement, so when that ask comes up you should say no 😆. Middleware can already do this. Also last minor nitpick: |
We actually decided on just a
Yeah, this proposal explicitly doesn't try to solve the global filters problem. For now, filters can be applied on routes (and in the future on route groups) but there is no scenario for global ones at the moment.
We anticipate that users will be able to modify both the existing arguments (e.g. uppercase all the string parameters to a method) and modify the response (e.g. change the error message to address the type mentioned above). I suspect that for most global scenarios middlewares would be sufficient...
Fair point. We can definitely adjust this in response to user feedback. I opted for "parameters" instead of "arguments" since that list semantically represents things like query parameters, route parameters, and body parameters to the route as opposed to the literal function arguments. |
Filters in MVC allow for code to be executed immediately before and after the invocation of action methods, allowing for inspection and modification of input parameters before execution, or even completely short-circuiting the action invocation altogether, along with inspection and modification of action method results, or even completely replacing the result, before they are executed.
There is no equivalent of this today when composing request handlers using raw endpoints however, e.g. via
app.MapGet
, etc. This issue is proposing adding support for filters as a new intrinsic in the request pipeline. We could consider having MVC support running filters registered via this new mechanism in addition to its own filters feature.There are two types of filter scenarios to consider:
Func<int, string, IResult>
. This scenario would require access to theBoth scenarios involve each filter invocation getting passed the delegate representing the next filter in the pipeline, such that they can decide whether or not to invoke the next filter or not.
Generic filter example
WIP, more to come
Scratch
The text was updated successfully, but these errors were encountered: