Update ExceptionHandler middleware pipeline construction to reinvoke UseRouting in error branch#34991
Update ExceptionHandler middleware pipeline construction to reinvoke UseRouting in error branch#34991JunTaoLuo wants to merge 6 commits into
Conversation
| app.UseRouting(); | ||
| app.UseRouting(overrideEndpointRouteBuilder: false); | ||
|
|
||
| // Copy the endpoint route builder to the built application |
There was a problem hiding this comment.
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
| } | ||
|
|
||
| return app.UseMiddleware<ExceptionHandlerMiddleware>(Options.Create(options)); | ||
| return app.Use(next => |
There was a problem hiding this comment.
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.
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.
overrideEndpointRouteBuilder..... is literal.
There was a problem hiding this comment.
Alternative ideas:
branchBuilderorbranchRouteBuilderreplaceBuilderorreplaceRouteBuilderoverrideBuilderoroverrideRouteBuilder- ?
There was a problem hiding this comment.
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.
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.
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.
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.
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.
Why do you need to change this? Can't you use the normal UseMiddleware<ExceptionHandlerMiddleware>?
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.
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.
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.
I'd rather do only what's necessary rather than do extra stuff. If there was a scenario this fixed, then fine.
| { | ||
| // 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.
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.
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.
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.
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.
| ((IConfigurationBuilder)Configuration).Sources.Clear(); | ||
| Configuration.AddConfiguration(_builtApplication.Configuration); | ||
|
|
||
| _builtApplication.Properties[WebApplicationBuilderKey] = true; |
There was a problem hiding this comment.
What's the purpose of this property? Trying to grok where it is used...
There was a problem hiding this comment.
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.
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. for the case when |
|
Closing in favor of #35426 |
Fixes #34146