Skip to content

Commit 2731ac7

Browse files
davidfowljaviercnKahbazipranavkm
authored
Improve linker friendliness for RouteOptions (#38014)
* Improve linker friendliness for RouteOptions - Added API to allows setting parameter policies in a linker friendly way - Added tests - Updated solution filter * Apply suggestions from code review Co-authored-by: Javier Calvarro Nelson <[email protected]> Co-authored-by: Kahbazi <[email protected]> * Apply suggestions from code review Co-authored-by: Pranav K <[email protected]> Co-authored-by: Javier Calvarro Nelson <[email protected]> Co-authored-by: Kahbazi <[email protected]> Co-authored-by: Pranav K <[email protected]>
1 parent 64de6d3 commit 2731ac7

File tree

4 files changed

+72
-3
lines changed

4 files changed

+72
-3
lines changed

src/Http/HttpAbstractions.slnf

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22
"solution": {
33
"path": "..\\..\\AspNetCore.sln",
44
"projects": [
5+
"src\\Extensions\\Features\\src\\Microsoft.Extensions.Features.csproj",
6+
"src\\Extensions\\Features\\test\\Microsoft.Extensions.Features.Tests.csproj",
57
"src\\Hosting\\Abstractions\\src\\Microsoft.AspNetCore.Hosting.Abstractions.csproj",
68
"src\\Hosting\\Hosting\\src\\Microsoft.AspNetCore.Hosting.csproj",
79
"src\\Hosting\\Server.Abstractions\\src\\Microsoft.AspNetCore.Hosting.Server.Abstractions.csproj",
810
"src\\Hosting\\TestHost\\src\\Microsoft.AspNetCore.TestHost.csproj",
911
"src\\Http\\Authentication.Abstractions\\src\\Microsoft.AspNetCore.Authentication.Abstractions.csproj",
1012
"src\\Http\\Authentication.Core\\src\\Microsoft.AspNetCore.Authentication.Core.csproj",
1113
"src\\Http\\Authentication.Core\\test\\Microsoft.AspNetCore.Authentication.Core.Test.csproj",
12-
"src\\Extensions\\Features\\src\\Microsoft.Extensions.Features.csproj",
13-
"src\\Extensions\\Features\\test\\Microsoft.Extensions.Features.Tests.csproj",
1414
"src\\Http\\Headers\\src\\Microsoft.Net.Http.Headers.csproj",
1515
"src\\Http\\Headers\\test\\Microsoft.Net.Http.Headers.Tests.csproj",
1616
"src\\Http\\Http.Abstractions\\perf\\Microbenchmarks\\Microsoft.AspNetCore.Http.Abstractions.Microbenchmarks.csproj",
@@ -46,6 +46,7 @@
4646
"src\\Middleware\\HttpOverrides\\src\\Microsoft.AspNetCore.HttpOverrides.csproj",
4747
"src\\Middleware\\StaticFiles\\src\\Microsoft.AspNetCore.StaticFiles.csproj",
4848
"src\\ObjectPool\\src\\Microsoft.Extensions.ObjectPool.csproj",
49+
"src\\Security\\Authorization\\Core\\src\\Microsoft.AspNetCore.Authorization.csproj",
4950
"src\\Security\\Authorization\\Policy\\src\\Microsoft.AspNetCore.Authorization.Policy.csproj",
5051
"src\\Servers\\Connections.Abstractions\\src\\Microsoft.AspNetCore.Connections.Abstractions.csproj",
5152
"src\\Servers\\IIS\\IISIntegration\\src\\Microsoft.AspNetCore.Server.IISIntegration.csproj",

src/Http/Routing/src/PublicAPI.Unshipped.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ Microsoft.AspNetCore.Routing.EndpointGroupNameAttribute.EndpointGroupName.get ->
2929
Microsoft.AspNetCore.Routing.EndpointNameAttribute
3030
Microsoft.AspNetCore.Routing.EndpointNameAttribute.EndpointNameAttribute(string! endpointName) -> void
3131
Microsoft.AspNetCore.Routing.EndpointNameAttribute.EndpointName.get -> string!
32+
Microsoft.AspNetCore.Routing.RouteOptions.SetParameterPolicy(string! token, System.Type! type) -> void
33+
Microsoft.AspNetCore.Routing.RouteOptions.SetParameterPolicy<T>(string! token) -> void
3234
static Microsoft.AspNetCore.Builder.EndpointRouteBuilderExtensions.Map(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! endpoints, Microsoft.AspNetCore.Routing.Patterns.RoutePattern! pattern, System.Delegate! handler) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder!
3335
static Microsoft.AspNetCore.Builder.EndpointRouteBuilderExtensions.Map(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! endpoints, string! pattern, System.Delegate! handler) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder!
3436
static Microsoft.AspNetCore.Builder.EndpointRouteBuilderExtensions.MapDelete(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! endpoints, string! pattern, System.Delegate! handler) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder!

src/Http/Routing/src/RouteOptions.cs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ internal ICollection<EndpointDataSource> EndpointDataSources
7272
/// </summary>
7373
public IDictionary<string, Type> ConstraintMap
7474
{
75+
[RequiresUnreferencedCode($"The linker cannot determine what constraints are being added via the ConstraintMap property. Prefer {nameof(RouteOptions)}.{nameof(SetParameterPolicy)} instead for setting constraints. This warning can be suppressed if this property is being used to read of delete constraints.")]
7576
get
7677
{
7778
return _constraintTypeMap;
@@ -124,7 +125,32 @@ private static IDictionary<string, Type> GetDefaultConstraintMap()
124125
return defaults;
125126
}
126127

127-
// This API could be exposed on RouteOptions
128+
/// <summary>
129+
/// Adds or overwrites the parameter policy with the associated route pattern token.
130+
/// </summary>
131+
/// <typeparam name="T">The parameter policy type.</typeparam>
132+
/// <param name="token">The route token used to apply the parameter policy.</param>
133+
public void SetParameterPolicy<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]T>(string token) where T : IParameterPolicy
134+
{
135+
ConstraintMap[token] = typeof(T);
136+
}
137+
138+
/// <summary>
139+
/// Adds or overwrites the parameter policy with the associated route pattern token.
140+
/// </summary>
141+
/// <param name="token">The route token used to apply the parameter policy.</param>
142+
/// <param name="type">The parameter policy type.</param>
143+
/// <exception cref="InvalidOperationException">Throws an exception if the type is not an <see cref="IParameterPolicy"/>.</exception>
144+
public void SetParameterPolicy(string token, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type type)
145+
{
146+
if (!type.IsAssignableTo(typeof(IParameterPolicy)))
147+
{
148+
throw new InvalidOperationException($"{type} must implement {typeof(IParameterPolicy)}");
149+
}
150+
151+
ConstraintMap[token] = type;
152+
}
153+
128154
private static void AddConstraint<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]TConstraint>(Dictionary<string, Type> constraintMap, string text) where TConstraint : IRouteConstraint
129155
{
130156
constraintMap[text] = typeof(TConstraint);

src/Http/Routing/test/UnitTests/DefaultInlineConstraintResolverTest.cs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,46 @@ public void ResolveConstraint_SupportsCustomConstraints()
269269
Assert.IsType<CustomRouteConstraint>(constraint);
270270
}
271271

272+
[Fact]
273+
public void ResolveConstraint_SupportsCustomConstraintsUsingNonGenericOverload()
274+
{
275+
// Arrange
276+
var routeOptions = new RouteOptions();
277+
routeOptions.SetParameterPolicy("custom", typeof(CustomRouteConstraint));
278+
var resolver = GetInlineConstraintResolver(routeOptions);
279+
280+
// Act
281+
var constraint = resolver.ResolveConstraint("custom(argument)");
282+
283+
// Assert
284+
Assert.IsType<CustomRouteConstraint>(constraint);
285+
}
286+
287+
[Fact]
288+
public void SetParameterPolicyThrowsIfTypeIsNotIParameterPolicy()
289+
{
290+
// Arrange
291+
var routeOptions = new RouteOptions();
292+
var ex = Assert.Throws<InvalidOperationException>(() => routeOptions.SetParameterPolicy("custom", typeof(string)));
293+
294+
Assert.Equal("System.String must implement Microsoft.AspNetCore.Routing.IParameterPolicy", ex.Message);
295+
}
296+
297+
[Fact]
298+
public void ResolveConstraint_SupportsCustomConstraintsUsingGenericOverloads()
299+
{
300+
// Arrange
301+
var routeOptions = new RouteOptions();
302+
routeOptions.SetParameterPolicy<CustomRouteConstraint>("custom");
303+
var resolver = GetInlineConstraintResolver(routeOptions);
304+
305+
// Act
306+
var constraint = resolver.ResolveConstraint("custom(argument)");
307+
308+
// Assert
309+
Assert.IsType<CustomRouteConstraint>(constraint);
310+
}
311+
272312
[Fact]
273313
public void ResolveConstraint_CustomConstraintThatDoesNotImplementIRouteConstraint_Throws()
274314
{

0 commit comments

Comments
 (0)