Skip to content

Improve linker friendliness for RouteOptions #38014

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/Http/HttpAbstractions.slnf
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
"solution": {
"path": "..\\..\\AspNetCore.sln",
"projects": [
"src\\Extensions\\Features\\src\\Microsoft.Extensions.Features.csproj",
"src\\Extensions\\Features\\test\\Microsoft.Extensions.Features.Tests.csproj",
"src\\Hosting\\Abstractions\\src\\Microsoft.AspNetCore.Hosting.Abstractions.csproj",
"src\\Hosting\\Hosting\\src\\Microsoft.AspNetCore.Hosting.csproj",
"src\\Hosting\\Server.Abstractions\\src\\Microsoft.AspNetCore.Hosting.Server.Abstractions.csproj",
"src\\Hosting\\TestHost\\src\\Microsoft.AspNetCore.TestHost.csproj",
"src\\Http\\Authentication.Abstractions\\src\\Microsoft.AspNetCore.Authentication.Abstractions.csproj",
"src\\Http\\Authentication.Core\\src\\Microsoft.AspNetCore.Authentication.Core.csproj",
"src\\Http\\Authentication.Core\\test\\Microsoft.AspNetCore.Authentication.Core.Test.csproj",
"src\\Extensions\\Features\\src\\Microsoft.Extensions.Features.csproj",
"src\\Extensions\\Features\\test\\Microsoft.Extensions.Features.Tests.csproj",
"src\\Http\\Headers\\src\\Microsoft.Net.Http.Headers.csproj",
"src\\Http\\Headers\\test\\Microsoft.Net.Http.Headers.Tests.csproj",
"src\\Http\\Http.Abstractions\\perf\\Microbenchmarks\\Microsoft.AspNetCore.Http.Abstractions.Microbenchmarks.csproj",
Expand Down Expand Up @@ -46,6 +46,7 @@
"src\\Middleware\\HttpOverrides\\src\\Microsoft.AspNetCore.HttpOverrides.csproj",
"src\\Middleware\\StaticFiles\\src\\Microsoft.AspNetCore.StaticFiles.csproj",
"src\\ObjectPool\\src\\Microsoft.Extensions.ObjectPool.csproj",
"src\\Security\\Authorization\\Core\\src\\Microsoft.AspNetCore.Authorization.csproj",
"src\\Security\\Authorization\\Policy\\src\\Microsoft.AspNetCore.Authorization.Policy.csproj",
"src\\Servers\\Connections.Abstractions\\src\\Microsoft.AspNetCore.Connections.Abstractions.csproj",
"src\\Servers\\IIS\\IISIntegration\\src\\Microsoft.AspNetCore.Server.IISIntegration.csproj",
Expand Down
2 changes: 2 additions & 0 deletions src/Http/Routing/src/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ Microsoft.AspNetCore.Routing.EndpointGroupNameAttribute.EndpointGroupName.get ->
Microsoft.AspNetCore.Routing.EndpointNameAttribute
Microsoft.AspNetCore.Routing.EndpointNameAttribute.EndpointNameAttribute(string! endpointName) -> void
Microsoft.AspNetCore.Routing.EndpointNameAttribute.EndpointName.get -> string!
Microsoft.AspNetCore.Routing.RouteOptions.SetParameterPolicy(string! token, System.Type! type) -> void
Microsoft.AspNetCore.Routing.RouteOptions.SetParameterPolicy<T>(string! token) -> void
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!
static Microsoft.AspNetCore.Builder.EndpointRouteBuilderExtensions.Map(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! endpoints, string! pattern, System.Delegate! handler) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder!
static Microsoft.AspNetCore.Builder.EndpointRouteBuilderExtensions.MapDelete(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! endpoints, string! pattern, System.Delegate! handler) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder!
Expand Down
28 changes: 27 additions & 1 deletion src/Http/Routing/src/RouteOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ internal ICollection<EndpointDataSource> EndpointDataSources
/// </summary>
public IDictionary<string, Type> ConstraintMap
{
[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.")]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't say linker in public facing docs/strings. Instead, we prefer the term trimmer or trimming.

is being used to read of delete constraints.

I'm not sure I understand what that means. Is there a type-o or missing word?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah hah

get
{
return _constraintTypeMap;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't you also need a RequiresUnreferencedCode attribute on the setter?

Expand Down Expand Up @@ -124,7 +125,32 @@ private static IDictionary<string, Type> GetDefaultConstraintMap()
return defaults;
}

// This API could be exposed on RouteOptions
/// <summary>
/// Adds or overwrites the parameter policy with the associated route pattern token.
/// </summary>
/// <typeparam name="T">The parameter policy type.</typeparam>
/// <param name="token">The route token used to apply the parameter policy.</param>
public void SetParameterPolicy<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]T>(string token) where T : IParameterPolicy
{
ConstraintMap[token] = typeof(T);
}

/// <summary>
/// Adds or overwrites the parameter policy with the associated route pattern token.
/// </summary>
/// <param name="token">The route token used to apply the parameter policy.</param>
/// <param name="type">The parameter policy type.</param>
/// <exception cref="InvalidOperationException">Throws an exception if the type is not an <see cref="IParameterPolicy"/>.</exception>
public void SetParameterPolicy(string token, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type type)
{
if (!type.IsAssignableTo(typeof(IParameterPolicy)))
{
throw new InvalidOperationException($"{type} must implement {typeof(IParameterPolicy)}");
}

ConstraintMap[token] = type;
}

private static void AddConstraint<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]TConstraint>(Dictionary<string, Type> constraintMap, string text) where TConstraint : IRouteConstraint
{
constraintMap[text] = typeof(TConstraint);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,46 @@ public void ResolveConstraint_SupportsCustomConstraints()
Assert.IsType<CustomRouteConstraint>(constraint);
}

[Fact]
public void ResolveConstraint_SupportsCustomConstraintsUsingNonGenericOverload()
{
// Arrange
var routeOptions = new RouteOptions();
routeOptions.SetParameterPolicy("custom", typeof(CustomRouteConstraint));
var resolver = GetInlineConstraintResolver(routeOptions);

// Act
var constraint = resolver.ResolveConstraint("custom(argument)");

// Assert
Assert.IsType<CustomRouteConstraint>(constraint);
}

[Fact]
public void SetParameterPolicyThrowsIfTypeIsNotIParameterPolicy()
{
// Arrange
var routeOptions = new RouteOptions();
var ex = Assert.Throws<InvalidOperationException>(() => routeOptions.SetParameterPolicy("custom", typeof(string)));

Assert.Equal("System.String must implement Microsoft.AspNetCore.Routing.IParameterPolicy", ex.Message);
}

[Fact]
public void ResolveConstraint_SupportsCustomConstraintsUsingGenericOverloads()
{
// Arrange
var routeOptions = new RouteOptions();
routeOptions.SetParameterPolicy<CustomRouteConstraint>("custom");
var resolver = GetInlineConstraintResolver(routeOptions);

// Act
var constraint = resolver.ResolveConstraint("custom(argument)");

// Assert
Assert.IsType<CustomRouteConstraint>(constraint);
}

[Fact]
public void ResolveConstraint_CustomConstraintThatDoesNotImplementIRouteConstraint_Throws()
{
Expand Down