44using System . Diagnostics ;
55using System . Reflection ;
66using Microsoft . AspNetCore . Hosting ;
7- using Microsoft . AspNetCore . Routing ;
87using Microsoft . Extensions . Configuration ;
98using Microsoft . Extensions . DependencyInjection ;
109using Microsoft . Extensions . Hosting ;
@@ -17,11 +16,10 @@ namespace Microsoft.AspNetCore.Builder
1716 /// </summary>
1817 public sealed class WebApplicationBuilder
1918 {
20- private const string EndpointRouteBuilderKey = "__EndpointRouteBuilder" ;
21-
2219 private readonly HostBuilder _hostBuilder = new ( ) ;
2320 private readonly BootstrapHostBuilder _bootstrapHostBuilder ;
2421 private readonly WebApplicationServiceCollection _services = new ( ) ;
22+ private const string EndpointRouteBuilderKey = "__EndpointRouteBuilder" ;
2523
2624 private WebApplication ? _builtApplication ;
2725
@@ -187,48 +185,39 @@ private void ConfigureApplication(WebHostBuilderContext context, IApplicationBui
187185 {
188186 Debug . Assert ( _builtApplication is not null ) ;
189187
188+ // UseRouting called before WebApplication such as in a StartupFilter
189+ // lets remove the property and reset it at the end so we don't mess with the routes in the filter
190+ if ( app . Properties . TryGetValue ( EndpointRouteBuilderKey , out var priorRouteBuilder ) )
191+ {
192+ app . Properties . Remove ( EndpointRouteBuilderKey ) ;
193+ }
194+
190195 if ( context . HostingEnvironment . IsDevelopment ( ) )
191196 {
197+ // TODO: add test for this
192198 app . UseDeveloperExceptionPage ( ) ;
193199 }
194200
195- var implicitRouting = false ;
201+ // Wrap the entire destination pipeline in UseRouting() and UseEndpoints(), essentially:
202+ // destination.UseRouting()
203+ // destination.Run(source)
204+ // destination.UseEndpoints()
205+
206+ // Set the route builder so that UseRouting will use the WebApplication as the IEndpointRouteBuilder for route matching
207+ app . Properties . Add ( WebApplication . GlobalEndpointRouteBuilderKey , _builtApplication ) ;
196208
197- // The endpoints were already added on the outside
209+ // Only call UseRouting() if there are endpoints configured and UseRouting() wasn't called on the global route builder already
198210 if ( _builtApplication . DataSources . Count > 0 )
199211 {
200- // The user did not register the routing middleware so wrap the entire
201- // destination pipeline in UseRouting() and UseEndpoints(), essentially:
202- // destination.UseRouting()
203- // destination.Run(source)
204- // destination.UseEndpoints()
205-
206- // Copy endpoints to the IEndpointRouteBuilder created by an explicit call to UseRouting() if possible.
207- var targetRouteBuilder = GetEndpointRouteBuilder ( _builtApplication ) ;
208-
209- if ( targetRouteBuilder is null )
212+ // If this is set, someone called UseRouting() when a global route builder was already set
213+ if ( ! _builtApplication . Properties . TryGetValue ( EndpointRouteBuilderKey , out var localRouteBuilder ) )
210214 {
211- // The app defined endpoints without calling UseRouting() explicitly, so call UseRouting() implicitly.
212215 app . UseRouting ( ) ;
213-
214- // An implicitly created IEndpointRouteBuilder was addeded to app.Properties by the UseRouting() call above.
215- targetRouteBuilder = GetEndpointRouteBuilder ( app ) ! ;
216- implicitRouting = true ;
217216 }
218-
219- // Copy the endpoints to the explicitly or implicitly created IEndopintRouteBuilder.
220- foreach ( var ds in _builtApplication . DataSources )
217+ else
221218 {
222- targetRouteBuilder . DataSources . Add ( ds ) ;
223- }
224-
225- // UseEndpoints consumes the DataSources immediately to populate CompositeEndpointDataSource via RouteOptions,
226- // so it must be called after we copy the endpoints.
227- if ( ! implicitRouting )
228- {
229- // UseRouting() was called explicitely, but we may still need to call UseEndpoints() implicitely at
230- // the end of the pipeline.
231- _builtApplication . UseEndpoints ( _ => { } ) ;
219+ // UseEndpoints will be looking for the RouteBuilder so make sure it's set
220+ app . Properties [ EndpointRouteBuilderKey ] = localRouteBuilder ;
232221 }
233222 }
234223
@@ -239,11 +228,9 @@ private void ConfigureApplication(WebHostBuilderContext context, IApplicationBui
239228 return _builtApplication . BuildRequestDelegate ( ) ;
240229 } ) ;
241230
242- // Implicitly call UseEndpoints() at the end of the pipeline if UseRouting() was called implicitly.
243- // We could add this to the end of _buildApplication instead if UseEndpoints() was not so picky about
244- // being called with the same IApplicationBluilder instance as UseRouting().
245- if ( implicitRouting )
231+ if ( _builtApplication . DataSources . Count > 0 )
246232 {
233+ // We don't know if user code called UseEndpoints(), so we will call it just in case, UseEndpoints() will ignore duplicate DataSources
247234 app . UseEndpoints ( _ => { } ) ;
248235 }
249236
@@ -252,12 +239,15 @@ private void ConfigureApplication(WebHostBuilderContext context, IApplicationBui
252239 {
253240 app . Properties [ item . Key ] = item . Value ;
254241 }
255- }
256242
257- private static IEndpointRouteBuilder ? GetEndpointRouteBuilder ( IApplicationBuilder app )
258- {
259- app . Properties . TryGetValue ( EndpointRouteBuilderKey , out var value ) ;
260- return ( IEndpointRouteBuilder ? ) value ;
243+ // Remove the route builder to clean up the properties, we're done adding routes to the pipeline
244+ app . Properties . Remove ( WebApplication . GlobalEndpointRouteBuilderKey ) ;
245+
246+ // reset route builder if it existed, this is needed for StartupFilters
247+ if ( priorRouteBuilder is not null )
248+ {
249+ app . Properties [ EndpointRouteBuilderKey ] = priorRouteBuilder ;
250+ }
261251 }
262252
263253 private class LoggingBuilder : ILoggingBuilder
0 commit comments