Skip to content

Commit 24fdfa9

Browse files
authored
Fix endpoints not being available in debug view until after WebApplication starts (#49103)
1 parent dd98b2e commit 24fdfa9

File tree

2 files changed

+76
-7
lines changed

2 files changed

+76
-7
lines changed

src/DefaultBuilder/src/WebApplication.cs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System.Diagnostics;
55
using System.Diagnostics.CodeAnalysis;
6+
using System.Linq;
67
using Microsoft.AspNetCore.Hosting;
78
using Microsoft.AspNetCore.Hosting.Server;
89
using Microsoft.AspNetCore.Hosting.Server.Features;
@@ -259,7 +260,30 @@ internal sealed class WebApplicationDebugView(WebApplication webApplication)
259260
public IHostApplicationLifetime Lifetime => _webApplication.Lifetime;
260261
public ILogger Logger => _webApplication.Logger;
261262
public string Urls => string.Join(", ", _webApplication.Urls);
262-
public IReadOnlyList<Endpoint> Endpoints => _webApplication.Services.GetRequiredService<EndpointDataSource>().Endpoints;
263+
public IReadOnlyList<Endpoint> Endpoints
264+
{
265+
get
266+
{
267+
var dataSource = _webApplication.Services.GetRequiredService<EndpointDataSource>();
268+
if (dataSource is CompositeEndpointDataSource compositeEndpointDataSource)
269+
{
270+
// The web app's data sources aren't registered until the routing middleware is. That often happens when the app is run.
271+
// We want endpoints to be available in the debug view before the app starts. Test if all the web app's the data sources are registered.
272+
if (compositeEndpointDataSource.DataSources.Intersect(_webApplication.DataSources).Count() == _webApplication.DataSources.Count)
273+
{
274+
// Data sources are centrally registered.
275+
return dataSource.Endpoints;
276+
}
277+
else
278+
{
279+
// Fallback to just the web app's data sources to support debugging before the web app starts.
280+
return new CompositeEndpointDataSource(_webApplication.DataSources).Endpoints;
281+
}
282+
}
283+
284+
return dataSource.Endpoints;
285+
}
286+
}
263287
public bool IsRunning => _webApplication.IsRunning;
264288
public IList<string>? Middleware
265289
{

src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebApplicationTests.cs

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
using Microsoft.AspNetCore.Hosting.Server.Features;
1818
using Microsoft.AspNetCore.Http;
1919
using Microsoft.AspNetCore.Http.Features;
20+
using Microsoft.AspNetCore.Http.Metadata;
2021
using Microsoft.AspNetCore.Routing;
2122
using Microsoft.AspNetCore.Routing.Constraints;
2223
using Microsoft.AspNetCore.TestHost;
@@ -2536,7 +2537,7 @@ private sealed class TestDebugger : IDebugger
25362537
}
25372538

25382539
[Fact]
2539-
public void UseMiddleware_DebugView_HasMiddleware()
2540+
public void DebugView_UseMiddleware_HasMiddleware()
25402541
{
25412542
var builder = WebApplication.CreateBuilder();
25422543
builder.Services.AddSingleton<IDebugger>(new TestDebugger(true));
@@ -2561,13 +2562,13 @@ public void UseMiddleware_DebugView_HasMiddleware()
25612562
m => Assert.Equal("Microsoft.AspNetCore.Authentication.AuthenticationMiddleware", m),
25622563
m =>
25632564
{
2564-
Assert.Contains(nameof(UseMiddleware_DebugView_HasMiddleware), m);
2565+
Assert.Contains(nameof(DebugView_UseMiddleware_HasMiddleware), m);
25652566
Assert.DoesNotContain(nameof(RequestDelegate), m);
25662567
});
25672568
}
25682569

25692570
[Fact]
2570-
public void NoDebugger_DebugView_NoMiddleware()
2571+
public void DebugView_NoDebugger_NoMiddleware()
25712572
{
25722573
var builder = WebApplication.CreateBuilder();
25732574
builder.Services.AddSingleton<IDebugger>(new TestDebugger(false));
@@ -2587,7 +2588,7 @@ public void NoDebugger_DebugView_NoMiddleware()
25872588
}
25882589

25892590
[Fact]
2590-
public async Task UseMiddleware_HasEndpointsAndAuth_Run_DebugView_HasAutomaticMiddleware()
2591+
public async Task DebugView_UseMiddleware_HasEndpointsAndAuth_Run_HasAutomaticMiddleware()
25912592
{
25922593
var builder = WebApplication.CreateBuilder();
25932594
builder.WebHost.UseTestServer();
@@ -2615,7 +2616,7 @@ public async Task UseMiddleware_HasEndpointsAndAuth_Run_DebugView_HasAutomaticMi
26152616
}
26162617

26172618
[Fact]
2618-
public async Task NoMiddleware_Run_DebugView_HasAutomaticMiddleware()
2619+
public async Task DebugView_NoMiddleware_Run_HasAutomaticMiddleware()
26192620
{
26202621
var builder = WebApplication.CreateBuilder();
26212622
builder.WebHost.UseTestServer();
@@ -2633,7 +2634,7 @@ public async Task NoMiddleware_Run_DebugView_HasAutomaticMiddleware()
26332634
}
26342635

26352636
[Fact]
2636-
public void NestedMiddleware_DebugView_OnlyContainsTopLevelMiddleware()
2637+
public void DebugView_NestedMiddleware_OnlyContainsTopLevelMiddleware()
26372638
{
26382639
var builder = WebApplication.CreateBuilder();
26392640
builder.Services.AddSingleton<IDebugger>(new TestDebugger(true));
@@ -2655,6 +2656,50 @@ public void NestedMiddleware_DebugView_OnlyContainsTopLevelMiddleware()
26552656
Assert.Equal(3, debugView.Middleware.Count);
26562657
}
26572658

2659+
[Fact]
2660+
public async Task DebugView_Endpoints_AvailableBeforeAndAfterStart()
2661+
{
2662+
var builder = WebApplication.CreateBuilder();
2663+
2664+
await using var app = builder.Build();
2665+
app.MapGet("/hello", () => "hello world");
2666+
2667+
var debugView = new WebApplication.WebApplicationDebugView(app);
2668+
2669+
Assert.Collection(debugView.Endpoints,
2670+
ep => Assert.Equal("/hello", ep.Metadata.GetRequiredMetadata<IRouteDiagnosticsMetadata>().Route));
2671+
2672+
// Starting the app registers endpoint data sources with routing.
2673+
_ = app.RunAsync();
2674+
2675+
Assert.Collection(debugView.Endpoints,
2676+
ep => Assert.Equal("/hello", ep.Metadata.GetRequiredMetadata<IRouteDiagnosticsMetadata>().Route));
2677+
}
2678+
2679+
[Fact]
2680+
public async Task DebugView_Endpoints_UseEndpoints_AvailableBeforeAndAfterStart()
2681+
{
2682+
var builder = WebApplication.CreateBuilder();
2683+
2684+
await using var app = builder.Build();
2685+
app.UseRouting();
2686+
app.UseEndpoints(endpoints =>
2687+
{
2688+
endpoints.MapGet("/hello", () => "hello world");
2689+
});
2690+
2691+
var debugView = new WebApplication.WebApplicationDebugView(app);
2692+
2693+
Assert.Collection(debugView.Endpoints,
2694+
ep => Assert.Equal("/hello", ep.Metadata.GetRequiredMetadata<IRouteDiagnosticsMetadata>().Route));
2695+
2696+
// Starting the app registers endpoint data sources with routing.
2697+
_ = app.RunAsync();
2698+
2699+
Assert.Collection(debugView.Endpoints,
2700+
ep => Assert.Equal("/hello", ep.Metadata.GetRequiredMetadata<IRouteDiagnosticsMetadata>().Route));
2701+
}
2702+
26582703
private class MiddlewareWithInterface : IMiddleware
26592704
{
26602705
public Task InvokeAsync(HttpContext context, RequestDelegate next)

0 commit comments

Comments
 (0)