Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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.")]
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