Skip to content

Commit 5eb02a4

Browse files
authored
Remove RequestDelegateFactory call from RouteEndpointBuilder (#42827)
1 parent 45279f2 commit 5eb02a4

14 files changed

+226
-210
lines changed

src/Http/Routing/src/Builder/EndpointRouteBuilderExtensions.cs

Lines changed: 38 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Diagnostics.CodeAnalysis;
5-
using System.Linq;
6-
using System.Reflection;
75
using Microsoft.AspNetCore.Http;
86
using Microsoft.AspNetCore.Routing;
97
using Microsoft.AspNetCore.Routing.Patterns;
@@ -152,10 +150,7 @@ public static IEndpointConventionBuilder MapMethods(
152150
{
153151
ArgumentNullException.ThrowIfNull(httpMethods);
154152

155-
var builder = endpoints.Map(RoutePatternFactory.Parse(pattern), requestDelegate);
156-
builder.WithDisplayName($"{pattern} HTTP: {string.Join(", ", httpMethods)}");
157-
builder.WithMetadata(new HttpMethodMetadata(httpMethods));
158-
return builder;
153+
return endpoints.Map(RoutePatternFactory.Parse(pattern), requestDelegate, httpMethods);
159154
}
160155

161156
/// <summary>
@@ -186,41 +181,21 @@ public static IEndpointConventionBuilder Map(
186181
this IEndpointRouteBuilder endpoints,
187182
RoutePattern pattern,
188183
RequestDelegate requestDelegate)
184+
{
185+
return Map(endpoints, pattern, requestDelegate, httpMethods: null);
186+
}
187+
188+
private static IEndpointConventionBuilder Map(
189+
this IEndpointRouteBuilder endpoints,
190+
RoutePattern pattern,
191+
RequestDelegate requestDelegate,
192+
IEnumerable<string>? httpMethods)
189193
{
190194
ArgumentNullException.ThrowIfNull(endpoints);
191195
ArgumentNullException.ThrowIfNull(pattern);
192196
ArgumentNullException.ThrowIfNull(requestDelegate);
193197

194-
const int defaultOrder = 0;
195-
196-
var builder = new RouteEndpointBuilder(
197-
requestDelegate,
198-
pattern,
199-
defaultOrder)
200-
{
201-
DisplayName = pattern.RawText ?? pattern.DebuggerToString(),
202-
};
203-
204-
// Add delegate attributes as metadata
205-
var attributes = requestDelegate.Method.GetCustomAttributes();
206-
207-
// This can be null if the delegate is a dynamic method or compiled from an expression tree
208-
if (attributes != null)
209-
{
210-
foreach (var attribute in attributes)
211-
{
212-
builder.Metadata.Add(attribute);
213-
}
214-
}
215-
216-
var dataSource = endpoints.DataSources.OfType<ModelEndpointDataSource>().FirstOrDefault();
217-
if (dataSource == null)
218-
{
219-
dataSource = new ModelEndpointDataSource();
220-
endpoints.DataSources.Add(dataSource);
221-
}
222-
223-
return dataSource.AddEndpointBuilder(builder);
198+
return endpoints.GetOrAddRouteEndpointDataSource().AddRequestDelegate(pattern, requestDelegate, httpMethods);
224199
}
225200

226201
/// <summary>
@@ -429,18 +404,38 @@ private static RouteHandlerBuilder Map(
429404
ArgumentNullException.ThrowIfNull(pattern);
430405
ArgumentNullException.ThrowIfNull(handler);
431406

432-
var dataSource = endpoints.DataSources.OfType<RouteEndpointDataSource>().FirstOrDefault();
433-
if (dataSource is null)
407+
return endpoints.GetOrAddRouteEndpointDataSource().AddRouteHandler(pattern, handler, httpMethods, isFallback);
408+
}
409+
410+
private static RouteEndpointDataSource GetOrAddRouteEndpointDataSource(this IEndpointRouteBuilder endpoints)
411+
{
412+
RouteEndpointDataSource? routeEndpointDataSource = null;
413+
414+
foreach (var dataSource in endpoints.DataSources)
434415
{
435-
var routeHandlerOptions = endpoints.ServiceProvider.GetService<IOptions<RouteHandlerOptions>>();
416+
if (dataSource is RouteEndpointDataSource foundDataSource)
417+
{
418+
routeEndpointDataSource = foundDataSource;
419+
break;
420+
}
421+
}
422+
423+
if (routeEndpointDataSource is null)
424+
{
425+
// ServiceProvider isn't nullable, but it is being called by methods that historically did not access this property, so we null check anyway.
426+
var routeHandlerOptions = endpoints.ServiceProvider?.GetService<IOptions<RouteHandlerOptions>>();
436427
var throwOnBadRequest = routeHandlerOptions?.Value.ThrowOnBadRequest ?? false;
437428

438-
dataSource = new RouteEndpointDataSource(endpoints.ServiceProvider, throwOnBadRequest);
439-
endpoints.DataSources.Add(dataSource);
429+
routeEndpointDataSource = new RouteEndpointDataSource(endpoints.ServiceProvider ?? EmptyServiceProvider.Instance, throwOnBadRequest);
430+
endpoints.DataSources.Add(routeEndpointDataSource);
440431
}
441432

442-
var conventions = dataSource.AddEndpoint(pattern, handler, httpMethods, isFallback);
433+
return routeEndpointDataSource;
434+
}
443435

444-
return new RouteHandlerBuilder(conventions);
436+
private sealed class EmptyServiceProvider : IServiceProvider
437+
{
438+
public static EmptyServiceProvider Instance { get; } = new EmptyServiceProvider();
439+
public object? GetService(Type serviceType) => null;
445440
}
446441
}

src/Http/Routing/src/Builder/RouteHandlerBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public sealed class RouteHandlerBuilder : IEndpointConventionBuilder
1515

1616
/// <summary>
1717
/// Instantiates a new <see cref="RouteHandlerBuilder" /> given a ThrowOnAddAfterEndpointBuiltConventionCollection from
18-
/// <see cref="RouteEndpointDataSource.AddEndpoint(Routing.Patterns.RoutePattern, Delegate, IEnumerable{string}?, bool)"/>.
18+
/// <see cref="RouteEndpointDataSource.AddRouteHandler(Routing.Patterns.RoutePattern, Delegate, IEnumerable{string}?, bool)"/>.
1919
/// </summary>
2020
/// <param name="conventions">The convention list returned from <see cref="RouteEndpointDataSource"/>.</param>
2121
internal RouteHandlerBuilder(ICollection<Action<EndpointBuilder>> conventions)

src/Http/Routing/src/ConfigureRouteHandlerOptions.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,16 @@ namespace Microsoft.AspNetCore.Routing;
88

99
internal sealed class ConfigureRouteHandlerOptions : IConfigureOptions<RouteHandlerOptions>
1010
{
11-
private readonly IHostEnvironment _environment;
11+
private readonly IHostEnvironment? _environment;
1212

13-
public ConfigureRouteHandlerOptions(IHostEnvironment environment)
13+
public ConfigureRouteHandlerOptions(IHostEnvironment? environment = null)
1414
{
1515
_environment = environment;
1616
}
1717

1818
public void Configure(RouteHandlerOptions options)
1919
{
20-
if (_environment.IsDevelopment())
20+
if (_environment?.IsDevelopment() ?? false)
2121
{
2222
options.ThrowOnBadRequest = true;
2323
}

src/Http/Routing/src/Microsoft.AspNetCore.Routing.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
<ItemGroup>
2828
<Compile Include="$(SharedSourceRoot)PropertyHelper\*.cs" />
2929
<Compile Include="$(SharedSourceRoot)RoslynUtils\TypeHelper.cs" />
30-
<Compile Include="$(SharedSourceRoot)RoslynUtils\GeneratedNameParser.cs" />
3130
<Compile Include="$(SharedSourceRoot)MediaType\ReadOnlyMediaTypeHeaderValue.cs" LinkBase="Shared" />
3231
<Compile Include="$(SharedSourceRoot)MediaType\HttpTokenParsingRule.cs" LinkBase="Shared" />
3332
<Compile Include="$(SharedSourceRoot)ApiExplorerTypes\*.cs" LinkBase="Shared" />

src/Http/Routing/src/ModelEndpointDataSource.cs

Lines changed: 0 additions & 38 deletions
This file was deleted.

src/Http/Routing/src/RouteEndpointBuilder.cs

Lines changed: 2 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using System.Diagnostics.CodeAnalysis;
54
using Microsoft.AspNetCore.Builder;
65
using Microsoft.AspNetCore.Http;
76
using Microsoft.AspNetCore.Routing.Patterns;
@@ -44,44 +43,18 @@ public RouteEndpointBuilder(
4443
}
4544

4645
/// <inheritdoc />
47-
[UnconditionalSuppressMessage("Trimmer", "IL2026",
48-
Justification = "We surface a RequireUnreferencedCode in AddEndpointFilter which is required to call unreferenced code here. The trimmer is unable to infer this.")]
4946
public override Endpoint Build()
5047
{
5148
if (RequestDelegate is null)
5249
{
5350
throw new InvalidOperationException($"{nameof(RequestDelegate)} must be specified to construct a {nameof(RouteEndpoint)}.");
5451
}
5552

56-
var requestDelegate = RequestDelegate;
57-
58-
// Only replace the RequestDelegate if filters have been applied to this builder and they were not already handled by RouteEndpointDataSource.
59-
// This affects other data sources like DefaultEndpointDataSource (this is people manually newing up a data source with a list of Endpoints),
60-
// ModelEndpointDataSource (Map(RoutePattern, RequestDelegate) and by extension MapHub, MapHealthChecks, etc...),
61-
// ActionEndpointDataSourceBase (MapControllers, MapRazorPages, etc...) and people with custom data sources or otherwise manually building endpoints
62-
// using this type. At the moment this class is sealed, so at the moment we do not need to concern ourselves with what derived types may be doing.
63-
if (EndpointFilterFactories is { Count: > 0 })
64-
{
65-
// Even with filters applied, RDF.Create() will return back the exact same RequestDelegate instance we pass in if filters decide not to modify the
66-
// invocation pipeline. We're just passing in a RequestDelegate so none of the fancy options pertaining to how the Delegate parameters are handled
67-
// do not matter.
68-
RequestDelegateFactoryOptions rdfOptions = new()
69-
{
70-
EndpointFilterFactories = EndpointFilterFactories,
71-
EndpointMetadata = Metadata,
72-
};
73-
74-
// We ignore the returned EndpointMetadata has been already populated since we passed in non-null EndpointMetadata.
75-
requestDelegate = RequestDelegateFactory.Create(requestDelegate, rdfOptions).RequestDelegate;
76-
}
77-
78-
var routeEndpoint = new RouteEndpoint(
79-
requestDelegate,
53+
return new RouteEndpoint(
54+
RequestDelegate,
8055
RoutePattern,
8156
Order,
8257
new EndpointMetadataCollection(Metadata),
8358
DisplayName);
84-
85-
return routeEndpoint;
8659
}
8760
}

0 commit comments

Comments
 (0)