-
Notifications
You must be signed in to change notification settings - Fork 10.3k
Update ExceptionHandler middleware pipeline construction to reinvoke UseRouting in error branch #34991
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
Conversation
app.UseRouting(); | ||
app.UseRouting(overrideEndpointRouteBuilder: false); | ||
|
||
// Copy the endpoint route builder to the built application |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a bit hacky but required to ensure the EndpointRouteBuilder is shared between _builtApplication
and app
. We should probably invest in a cleaner approach for sharing the Properties
bag between the two builders.
… UseRouting in error branch
7482973
to
575aa50
Compare
@@ -95,7 +99,22 @@ public static IApplicationBuilder UseExceptionHandler(this IApplicationBuilder a | |||
throw new ArgumentNullException(nameof(options)); | |||
} | |||
|
|||
return app.UseMiddleware<ExceptionHandlerMiddleware>(Options.Create(options)); | |||
return app.Use(next => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All middlewares that modifies paths will now need to follow this pattern to work for Minimal APIs. I don't think this is very intuitive though.
Also, a similar fix is likely needed for RewriteMiddleware
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This also needs to be more specific, we probably need to distinguish between minimal API vs regular apps to figure out if we need to handle the middleware differently.
/// <see cref="Endpoint"/> associated with the <see cref="HttpContext"/>. | ||
/// </para> | ||
/// </remarks> | ||
public static IApplicationBuilder UseRouting(this IApplicationBuilder builder, bool overrideEndpointRouteBuilder) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
overrideEndpointRouteBuilder..... is literal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternative ideas:
branchBuilder
orbranchRouteBuilder
replaceBuilder
orreplaceRouteBuilder
overrideBuilder
oroverrideRouteBuilder
- ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think if we agree that this approach makes sense, we can discuss the naming in the API review. I think I like the overrideRouteBuilder
option the best though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The bool doesn't seem like a meaningful public parameter, anyone that wants the 'true' behavior will already be calling UseRouting()
.
This should be a separate method name and the bool would be an implementation detail.
UseRoutingAgain()
(🚲 🐑)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we not make this an extension method and leave it as a plain old static method? I don't expect many people to want to call this overload.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was actually one of my earlier ideas! I wanted to call it app.ReRoute() or app.RerunRouting() or app.UseRerouting().
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the idea of a different name. ResumeRouting?
options.ExceptionHandler = errorBuilder.Build(); | ||
} | ||
|
||
return new ExceptionHandlerMiddleware(next, loggerFactory, Options.Create(options), diagnosticListener).Invoke; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you need to change this? Can't you use the normal UseMiddleware<ExceptionHandlerMiddleware>
?
src/Http/Routing/src/Builder/EndpointRoutingApplicationBuilderExtensions.cs
Outdated
Show resolved
Hide resolved
90450b1
to
596f8b7
Compare
I'll flip this to a non-draft PR but I expect there to be a few rounds of discussions regarding the design. |
foreach (var item in app.Properties) | ||
{ | ||
_builtApplication.Properties[item.Key] = item.Value; | ||
} | ||
|
||
// An implicitly created IEndpointRouteBuilder was addeded to app.Properties by the UseRouting() call above. | ||
targetRouteBuilder = GetEndpointRouteBuilder(app)!; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels a little less hacky. This works, right?
foreach (var item in app.Properties) | |
{ | |
_builtApplication.Properties[item.Key] = item.Value; | |
} | |
// An implicitly created IEndpointRouteBuilder was addeded to app.Properties by the UseRouting() call above. | |
targetRouteBuilder = GetEndpointRouteBuilder(app)!; | |
// An implicitly created IEndpointRouteBuilder was addeded to app.Properties by the UseRouting() call above. | |
targetRouteBuilder = GetEndpointRouteBuilder(app)!; | |
// Copy the endpoint route builder to the built application | |
_builtApplication.Properties[EndpointRouteBuilderKey] = targetRouteBuilder; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it'd work but I'm curious why that's better? Adding specific item to the Property
bag still seems "hacky". I'm curious if we should keep the property bags on _builtApplication
and app
in sync? Would that be crossing too many wires?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather do only what's necessary rather than do extra stuff. If there was a scenario this fixed, then fine.
@@ -202,7 +205,13 @@ private void ConfigureApplication(WebHostBuilderContext context, IApplicationBui | |||
if (targetRouteBuilder is null) | |||
{ | |||
// The app defined endpoints without calling UseRouting() explicitly, so call UseRouting() implicitly. | |||
app.UseRouting(); | |||
app.UseRouting(overrideEndpointRouteBuilder: false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need to call this overload? We know there's no IEndpointRouteBuilder in this case, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not quite. UseExceptionHandler
is added by the user in Program.cs which occurs before app.Run()
which is where this configuration runs. So UseRouting()
here is configuring an EndpointRoutingMiddleware
before the ExceptionHandlerMiddleware
in the pipeline but doing so after UseExceptionHandler
is called.
Let me know if I need to be clearer since it's all a bit magical.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure why it matters when UseExceptionHandler
is called since it's looking for "__WebApplicationBuilder" anyway and that's always defined. The ExceptionHandlerMiddleware will always be added later in the pipeline with the call _builtApplication.BuildRequestDelegate()
.
Can you provide a test case where there's already an IEndpointRouteBuilder
in app.Properties
here? It's not easy for code in Program.cs to get access to this app
instance. The only thing I could think of is an IStartupFilter
or something like that. If we really care about a startup filter calling app.UseRouting()
before calling next(app)
, we could skip calling UseRouting()
altogether. I'm not convinced that's realistic though, so I'm not sure it's worth complicating the code for for this. If we do, we should add a regression test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm now I think about it, I don't recall why I set the argument to false here. However, I do clearly recall without it, things didn't work. I could debug it to come up with a more clear explanation.
That being said, after chatting with @javiercn today, I'm not sure if this is the approach we are going to take so I think it's a moot point. I think there's more investigation/design to be done before addressing this specific feedback.
@@ -170,6 +171,8 @@ public WebApplication Build() | |||
((IConfigurationBuilder)Configuration).Sources.Clear(); | |||
Configuration.AddConfiguration(_builtApplication.Configuration); | |||
|
|||
_builtApplication.Properties[WebApplicationBuilderKey] = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the purpose of this property? Trying to grok where it is used...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nevermind! I see you are using this in the extension methods to check if they are invoked on a WebApplicationBuilder. Correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I added this "hack" to clearly indicate that the builder is a WebApplicationBuilder, where we make use of "creative" pipeline construction techniques 😄
This method also needs to be updated. aspnetcore/src/Middleware/Diagnostics/src/ExceptionHandler/ExceptionHandlerExtensions.cs Lines 26 to 34 in 1f2d58b
for the case when Lines 20 to 32 in 1f2d58b
|
Closing in favor of #35426 |
Fixes #34146