4
4
using System . Diagnostics ;
5
5
using System . Reflection ;
6
6
using Microsoft . AspNetCore . Hosting ;
7
- using Microsoft . AspNetCore . Routing ;
8
7
using Microsoft . Extensions . Configuration ;
9
8
using Microsoft . Extensions . DependencyInjection ;
10
9
using Microsoft . Extensions . Hosting ;
@@ -17,11 +16,10 @@ namespace Microsoft.AspNetCore.Builder
17
16
/// </summary>
18
17
public sealed class WebApplicationBuilder
19
18
{
20
- private const string EndpointRouteBuilderKey = "__EndpointRouteBuilder" ;
21
-
22
19
private readonly HostBuilder _hostBuilder = new ( ) ;
23
20
private readonly BootstrapHostBuilder _bootstrapHostBuilder ;
24
21
private readonly WebApplicationServiceCollection _services = new ( ) ;
22
+ private const string EndpointRouteBuilderKey = "__EndpointRouteBuilder" ;
25
23
26
24
private WebApplication ? _builtApplication ;
27
25
@@ -187,48 +185,39 @@ private void ConfigureApplication(WebHostBuilderContext context, IApplicationBui
187
185
{
188
186
Debug . Assert ( _builtApplication is not null ) ;
189
187
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
+
190
195
if ( context . HostingEnvironment . IsDevelopment ( ) )
191
196
{
197
+ // TODO: add test for this
192
198
app . UseDeveloperExceptionPage ( ) ;
193
199
}
194
200
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 ) ;
196
208
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
198
210
if ( _builtApplication . DataSources . Count > 0 )
199
211
{
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 ) )
210
214
{
211
- // The app defined endpoints without calling UseRouting() explicitly, so call UseRouting() implicitly.
212
215
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 ;
217
216
}
218
-
219
- // Copy the endpoints to the explicitly or implicitly created IEndopintRouteBuilder.
220
- foreach ( var ds in _builtApplication . DataSources )
217
+ else
221
218
{
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 ;
232
221
}
233
222
}
234
223
@@ -239,11 +228,9 @@ private void ConfigureApplication(WebHostBuilderContext context, IApplicationBui
239
228
return _builtApplication . BuildRequestDelegate ( ) ;
240
229
} ) ;
241
230
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 )
246
232
{
233
+ // We don't know if user code called UseEndpoints(), so we will call it just in case, UseEndpoints() will ignore duplicate DataSources
247
234
app . UseEndpoints ( _ => { } ) ;
248
235
}
249
236
@@ -252,12 +239,15 @@ private void ConfigureApplication(WebHostBuilderContext context, IApplicationBui
252
239
{
253
240
app . Properties [ item . Key ] = item . Value ;
254
241
}
255
- }
256
242
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
+ }
261
251
}
262
252
263
253
private class LoggingBuilder : ILoggingBuilder
0 commit comments