Add IParameterTransformer support (#8329)
This commit is contained in:
parent
105f8b47a1
commit
dfae9c208a
|
|
@ -110,7 +110,8 @@ namespace Microsoft.AspNetCore.Mvc.Performance
|
||||||
{
|
{
|
||||||
var dataSource = new MvcEndpointDataSource(
|
var dataSource = new MvcEndpointDataSource(
|
||||||
actionDescriptorCollectionProvider,
|
actionDescriptorCollectionProvider,
|
||||||
new MvcEndpointInvokerFactory(new ActionInvokerFactory(Array.Empty<IActionInvokerProvider>())));
|
new MvcEndpointInvokerFactory(new ActionInvokerFactory(Array.Empty<IActionInvokerProvider>())),
|
||||||
|
new MockParameterPolicyFactory());
|
||||||
|
|
||||||
return dataSource;
|
return dataSource;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,8 +48,8 @@
|
||||||
<MicrosoftAspNetCoreRazorTagHelpersTestingSourcesPackageVersion>2.2.0-preview3-35202</MicrosoftAspNetCoreRazorTagHelpersTestingSourcesPackageVersion>
|
<MicrosoftAspNetCoreRazorTagHelpersTestingSourcesPackageVersion>2.2.0-preview3-35202</MicrosoftAspNetCoreRazorTagHelpersTestingSourcesPackageVersion>
|
||||||
<MicrosoftAspNetCoreResponseCachingAbstractionsPackageVersion>2.2.0-preview3-35202</MicrosoftAspNetCoreResponseCachingAbstractionsPackageVersion>
|
<MicrosoftAspNetCoreResponseCachingAbstractionsPackageVersion>2.2.0-preview3-35202</MicrosoftAspNetCoreResponseCachingAbstractionsPackageVersion>
|
||||||
<MicrosoftAspNetCoreResponseCachingPackageVersion>2.2.0-preview3-35202</MicrosoftAspNetCoreResponseCachingPackageVersion>
|
<MicrosoftAspNetCoreResponseCachingPackageVersion>2.2.0-preview3-35202</MicrosoftAspNetCoreResponseCachingPackageVersion>
|
||||||
<MicrosoftAspNetCoreRoutingAbstractionsPackageVersion>2.2.0-preview3-35202</MicrosoftAspNetCoreRoutingAbstractionsPackageVersion>
|
<MicrosoftAspNetCoreRoutingAbstractionsPackageVersion>2.2.0-a-preview3-parameter-transformers-16968</MicrosoftAspNetCoreRoutingAbstractionsPackageVersion>
|
||||||
<MicrosoftAspNetCoreRoutingPackageVersion>2.2.0-preview3-35202</MicrosoftAspNetCoreRoutingPackageVersion>
|
<MicrosoftAspNetCoreRoutingPackageVersion>2.2.0-a-preview3-parameter-transformers-16968</MicrosoftAspNetCoreRoutingPackageVersion>
|
||||||
<MicrosoftAspNetCoreServerIISIntegrationPackageVersion>2.2.0-preview3-35202</MicrosoftAspNetCoreServerIISIntegrationPackageVersion>
|
<MicrosoftAspNetCoreServerIISIntegrationPackageVersion>2.2.0-preview3-35202</MicrosoftAspNetCoreServerIISIntegrationPackageVersion>
|
||||||
<MicrosoftAspNetCoreServerKestrelPackageVersion>2.2.0-preview3-35202</MicrosoftAspNetCoreServerKestrelPackageVersion>
|
<MicrosoftAspNetCoreServerKestrelPackageVersion>2.2.0-preview3-35202</MicrosoftAspNetCoreServerKestrelPackageVersion>
|
||||||
<MicrosoftAspNetCoreSessionPackageVersion>2.2.0-preview3-35202</MicrosoftAspNetCoreSessionPackageVersion>
|
<MicrosoftAspNetCoreSessionPackageVersion>2.2.0-preview3-35202</MicrosoftAspNetCoreSessionPackageVersion>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.Routing
|
namespace Microsoft.AspNetCore.Mvc.Routing
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Microsoft.AspNetCore.Mvc.Core;
|
using Microsoft.AspNetCore.Mvc.Core;
|
||||||
using Microsoft.AspNetCore.Mvc.Routing;
|
using Microsoft.AspNetCore.Mvc.Routing;
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
||||||
{
|
{
|
||||||
|
|
@ -220,6 +221,11 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string ReplaceTokens(string template, IDictionary<string, string> values)
|
public static string ReplaceTokens(string template, IDictionary<string, string> values)
|
||||||
|
{
|
||||||
|
return ReplaceTokens(template, values, routeTokenTransformer: null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ReplaceTokens(string template, IDictionary<string, string> values, IParameterTransformer routeTokenTransformer)
|
||||||
{
|
{
|
||||||
var builder = new StringBuilder();
|
var builder = new StringBuilder();
|
||||||
var state = TemplateParserState.Plaintext;
|
var state = TemplateParserState.Plaintext;
|
||||||
|
|
@ -371,6 +377,11 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
||||||
throw new InvalidOperationException(message);
|
throw new InvalidOperationException(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (routeTokenTransformer != null)
|
||||||
|
{
|
||||||
|
value = routeTokenTransformer.Transform(value);
|
||||||
|
}
|
||||||
|
|
||||||
builder.Append(value);
|
builder.Append(value);
|
||||||
|
|
||||||
if (c == '[')
|
if (c == '[')
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An <see cref="IActionModelConvention"/> that sets attribute routing token replacement
|
||||||
|
/// to use the specified <see cref="IParameterTransformer"/> on <see cref="ActionModel"/> selectors.
|
||||||
|
/// </summary>
|
||||||
|
public class RouteTokenTransformerConvention : IActionModelConvention
|
||||||
|
{
|
||||||
|
private readonly IParameterTransformer _parameterTransformer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of <see cref="RouteTokenTransformerConvention"/> with the specified <see cref="IParameterTransformer"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parameterTransformer">The <see cref="IParameterTransformer"/> to use with attribute routing token replacement.</param>
|
||||||
|
public RouteTokenTransformerConvention(IParameterTransformer parameterTransformer)
|
||||||
|
{
|
||||||
|
if (parameterTransformer == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(parameterTransformer));
|
||||||
|
}
|
||||||
|
|
||||||
|
_parameterTransformer = parameterTransformer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Apply(ActionModel action)
|
||||||
|
{
|
||||||
|
if (ShouldApply(action))
|
||||||
|
{
|
||||||
|
action.Properties[typeof(IParameterTransformer)] = _parameterTransformer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual bool ShouldApply(ActionModel action) => true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -12,6 +12,7 @@ using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
using Microsoft.AspNetCore.Mvc.Routing;
|
using Microsoft.AspNetCore.Mvc.Routing;
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
using Resources = Microsoft.AspNetCore.Mvc.Core.Resources;
|
using Resources = Microsoft.AspNetCore.Mvc.Core.Resources;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.Internal
|
namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
|
|
@ -389,15 +390,20 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
actionDescriptor.Properties.TryGetValue(typeof(IParameterTransformer), out var transformer);
|
||||||
|
var routeTokenTransformer = transformer as IParameterTransformer;
|
||||||
|
|
||||||
actionDescriptor.AttributeRouteInfo.Template = AttributeRouteModel.ReplaceTokens(
|
actionDescriptor.AttributeRouteInfo.Template = AttributeRouteModel.ReplaceTokens(
|
||||||
actionDescriptor.AttributeRouteInfo.Template,
|
actionDescriptor.AttributeRouteInfo.Template,
|
||||||
actionDescriptor.RouteValues);
|
actionDescriptor.RouteValues,
|
||||||
|
routeTokenTransformer);
|
||||||
|
|
||||||
if (actionDescriptor.AttributeRouteInfo.Name != null)
|
if (actionDescriptor.AttributeRouteInfo.Name != null)
|
||||||
{
|
{
|
||||||
actionDescriptor.AttributeRouteInfo.Name = AttributeRouteModel.ReplaceTokens(
|
actionDescriptor.AttributeRouteInfo.Name = AttributeRouteModel.ReplaceTokens(
|
||||||
actionDescriptor.AttributeRouteInfo.Name,
|
actionDescriptor.AttributeRouteInfo.Name,
|
||||||
actionDescriptor.RouteValues);
|
actionDescriptor.RouteValues,
|
||||||
|
routeTokenTransformer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (InvalidOperationException ex)
|
catch (InvalidOperationException ex)
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
{
|
{
|
||||||
private readonly IActionDescriptorCollectionProvider _actions;
|
private readonly IActionDescriptorCollectionProvider _actions;
|
||||||
private readonly MvcEndpointInvokerFactory _invokerFactory;
|
private readonly MvcEndpointInvokerFactory _invokerFactory;
|
||||||
|
private readonly ParameterPolicyFactory _parameterPolicyFactory;
|
||||||
|
|
||||||
// The following are protected by this lock for WRITES only. This pattern is similar
|
// The following are protected by this lock for WRITES only. This pattern is similar
|
||||||
// to DefaultActionDescriptorChangeProvider - see comments there for details on
|
// to DefaultActionDescriptorChangeProvider - see comments there for details on
|
||||||
|
|
@ -33,7 +34,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
|
|
||||||
public MvcEndpointDataSource(
|
public MvcEndpointDataSource(
|
||||||
IActionDescriptorCollectionProvider actions,
|
IActionDescriptorCollectionProvider actions,
|
||||||
MvcEndpointInvokerFactory invokerFactory)
|
MvcEndpointInvokerFactory invokerFactory,
|
||||||
|
ParameterPolicyFactory parameterPolicyFactory)
|
||||||
{
|
{
|
||||||
if (actions == null)
|
if (actions == null)
|
||||||
{
|
{
|
||||||
|
|
@ -45,8 +47,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
throw new ArgumentNullException(nameof(invokerFactory));
|
throw new ArgumentNullException(nameof(invokerFactory));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (parameterPolicyFactory == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(parameterPolicyFactory));
|
||||||
|
}
|
||||||
|
|
||||||
_actions = actions;
|
_actions = actions;
|
||||||
_invokerFactory = invokerFactory;
|
_invokerFactory = invokerFactory;
|
||||||
|
_parameterPolicyFactory = parameterPolicyFactory;
|
||||||
|
|
||||||
ConventionalEndpointInfos = new List<MvcEndpointInfo>();
|
ConventionalEndpointInfos = new List<MvcEndpointInfo>();
|
||||||
|
|
||||||
|
|
@ -253,9 +261,28 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
{
|
{
|
||||||
segmentParts = segment.Parts.ToList();
|
segmentParts = segment.Parts.ToList();
|
||||||
}
|
}
|
||||||
|
if (allParameterPolicies == null)
|
||||||
|
{
|
||||||
|
allParameterPolicies = MvcEndpointInfo.BuildParameterPolicies(routePattern.Parameters, _parameterPolicyFactory);
|
||||||
|
}
|
||||||
|
|
||||||
var parameterRouteValue = action.RouteValues[parameterPart.Name];
|
var parameterRouteValue = action.RouteValues[parameterPart.Name];
|
||||||
|
|
||||||
|
// Replace parameter with literal value
|
||||||
|
if (allParameterPolicies.TryGetValue(parameterPart.Name, out var parameterPolicies))
|
||||||
|
{
|
||||||
|
// Check if the parameter has a transformer policy
|
||||||
|
// Use the first transformer policy
|
||||||
|
for (var k = 0; k < parameterPolicies.Count; k++)
|
||||||
|
{
|
||||||
|
if (parameterPolicies[k] is IParameterTransformer parameterTransformer)
|
||||||
|
{
|
||||||
|
parameterRouteValue = parameterTransformer.Transform(parameterRouteValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
segmentParts[j] = RoutePatternFactory.LiteralPart(parameterRouteValue);
|
segmentParts[j] = RoutePatternFactory.LiteralPart(parameterRouteValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.Test.ApplicationModel
|
||||||
|
{
|
||||||
|
public class RouteTokenTransformerConventionTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Apply_NullAttributeRouteModel_NoOp()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var convention = new RouteTokenTransformerConvention(new TestParameterTransformer());
|
||||||
|
|
||||||
|
var model = new ActionModel(GetMethodInfo(), Array.Empty<object>());
|
||||||
|
model.Selectors.Add(new SelectorModel()
|
||||||
|
{
|
||||||
|
AttributeRouteModel = null
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
convention.Apply(model);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Null(model.Selectors[0].AttributeRouteModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Apply_HasAttributeRouteModel_SetRouteTokenTransformer()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var transformer = new TestParameterTransformer();
|
||||||
|
var convention = new RouteTokenTransformerConvention(transformer);
|
||||||
|
|
||||||
|
var model = new ActionModel(GetMethodInfo(), Array.Empty<object>());
|
||||||
|
model.Selectors.Add(new SelectorModel()
|
||||||
|
{
|
||||||
|
AttributeRouteModel = new AttributeRouteModel()
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
convention.Apply(model);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(model.Properties.TryGetValue(typeof(IParameterTransformer), out var routeTokenTransformer));
|
||||||
|
Assert.Equal(transformer, routeTokenTransformer);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Apply_ShouldApplyFalse_NoOp()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var transformer = new TestParameterTransformer();
|
||||||
|
var convention = new CustomRouteTokenTransformerConvention(transformer);
|
||||||
|
|
||||||
|
var model = new ActionModel(GetMethodInfo(), Array.Empty<object>());
|
||||||
|
model.Selectors.Add(new SelectorModel()
|
||||||
|
{
|
||||||
|
AttributeRouteModel = new AttributeRouteModel()
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
convention.Apply(model);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(model.Properties.TryGetValue(typeof(IParameterTransformer), out _));
|
||||||
|
}
|
||||||
|
|
||||||
|
private MethodInfo GetMethodInfo()
|
||||||
|
{
|
||||||
|
return typeof(RouteTokenTransformerConventionTest).GetMethod(nameof(GetMethodInfo), BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestParameterTransformer : IParameterTransformer
|
||||||
|
{
|
||||||
|
public string Transform(string value)
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CustomRouteTokenTransformerConvention : RouteTokenTransformerConvention
|
||||||
|
{
|
||||||
|
public CustomRouteTokenTransformerConvention(IParameterTransformer parameterTransformer) : base(parameterTransformer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool ShouldApply(ActionModel action)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -161,6 +161,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
{"{controller}/{action=TestAction}/{id?}/{*catchAll}", new[] { "TestController", "TestController/TestAction/{id?}/{*catchAll}" }},
|
{"{controller}/{action=TestAction}/{id?}/{*catchAll}", new[] { "TestController", "TestController/TestAction/{id?}/{*catchAll}" }},
|
||||||
{"{controller}/{action}.{ext?}", new[] { "TestController/TestAction.{ext?}" }},
|
{"{controller}/{action}.{ext?}", new[] { "TestController/TestAction.{ext?}" }},
|
||||||
{"{controller}/{action=TestAction}.{ext?}", new[] { "TestController", "TestController/TestAction.{ext?}" }},
|
{"{controller}/{action=TestAction}.{ext?}", new[] { "TestController", "TestController/TestAction.{ext?}" }},
|
||||||
|
{"{controller:upper-case}/{action=TestAction}.{ext?}", new[] { "TESTCONTROLLER", "TESTCONTROLLER/TestAction.{ext?}" }},
|
||||||
};
|
};
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
|
|
@ -217,6 +218,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
[InlineData("{area=TestArea}/{controller}/{action=TestAction}/{id?}", new[] { "TestArea/TestController", "TestArea/TestController/TestAction/{id?}" })]
|
[InlineData("{area=TestArea}/{controller}/{action=TestAction}/{id?}", new[] { "TestArea/TestController", "TestArea/TestController/TestAction/{id?}" })]
|
||||||
[InlineData("{area=TestArea}/{controller=TestController}/{action=TestAction}/{id?}", new[] { "", "TestArea", "TestArea/TestController", "TestArea/TestController/TestAction/{id?}" })]
|
[InlineData("{area=TestArea}/{controller=TestController}/{action=TestAction}/{id?}", new[] { "", "TestArea", "TestArea/TestController", "TestArea/TestController/TestAction/{id?}" })]
|
||||||
[InlineData("{area:exists}/{controller}/{action}/{id?}", new[] { "TestArea/TestController/TestAction/{id?}" })]
|
[InlineData("{area:exists}/{controller}/{action}/{id?}", new[] { "TestArea/TestController/TestAction/{id?}" })]
|
||||||
|
[InlineData("{area:exists:upper-case}/{controller}/{action}/{id?}", new[] { "TESTAREA/TestController/TestAction/{id?}" })]
|
||||||
public void Endpoints_AreaSingleAction(string endpointInfoRoute, string[] finalEndpointTemplates)
|
public void Endpoints_AreaSingleAction(string endpointInfoRoute, string[] finalEndpointTemplates)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
|
@ -230,6 +232,10 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
|
|
||||||
var routeOptionsSetup = new MvcCoreRouteOptionsSetup();
|
var routeOptionsSetup = new MvcCoreRouteOptionsSetup();
|
||||||
services.Configure<RouteOptions>(routeOptionsSetup.Configure);
|
services.Configure<RouteOptions>(routeOptionsSetup.Configure);
|
||||||
|
services.Configure<RouteOptions>(options =>
|
||||||
|
{
|
||||||
|
options.ConstraintMap["upper-case"] = typeof(UpperCaseParameterTransform);
|
||||||
|
});
|
||||||
|
|
||||||
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(string.Empty, endpointInfoRoute, serviceProvider: services.BuildServiceProvider()));
|
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(string.Empty, endpointInfoRoute, serviceProvider: services.BuildServiceProvider()));
|
||||||
|
|
||||||
|
|
@ -377,6 +383,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData("{controller}/{action}", new[] { "TestController1/TestAction1", "TestController1/TestAction2", "TestController1/TestAction3", "TestController2/TestAction1" })]
|
[InlineData("{controller}/{action}", new[] { "TestController1/TestAction1", "TestController1/TestAction2", "TestController1/TestAction3", "TestController2/TestAction1" })]
|
||||||
[InlineData("{controller}/{action:regex((TestAction1|TestAction2))}", new[] { "TestController1/TestAction1", "TestController1/TestAction2", "TestController2/TestAction1" })]
|
[InlineData("{controller}/{action:regex((TestAction1|TestAction2))}", new[] { "TestController1/TestAction1", "TestController1/TestAction2", "TestController2/TestAction1" })]
|
||||||
|
[InlineData("{controller}/{action:regex((TestAction1|TestAction2)):upper-case}", new[] { "TestController1/TESTACTION1", "TestController1/TESTACTION2", "TestController2/TESTACTION1" })]
|
||||||
public void Endpoints_MultipleActions(string endpointInfoRoute, string[] finalEndpointTemplates)
|
public void Endpoints_MultipleActions(string endpointInfoRoute, string[] finalEndpointTemplates)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
|
@ -750,14 +757,28 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
|
|
||||||
var services = new ServiceCollection();
|
var services = new ServiceCollection();
|
||||||
services.AddSingleton(actionDescriptorCollectionProvider);
|
services.AddSingleton(actionDescriptorCollectionProvider);
|
||||||
|
services.AddRouting(options =>
|
||||||
|
{
|
||||||
|
options.ConstraintMap["upper-case"] = typeof(UpperCaseParameterTransform);
|
||||||
|
});
|
||||||
|
var serviceProvider = services.BuildServiceProvider();
|
||||||
|
|
||||||
var dataSource = new MvcEndpointDataSource(
|
var dataSource = new MvcEndpointDataSource(
|
||||||
actionDescriptorCollectionProvider,
|
actionDescriptorCollectionProvider,
|
||||||
mvcEndpointInvokerFactory ?? new MvcEndpointInvokerFactory(new ActionInvokerFactory(Array.Empty<IActionInvokerProvider>())));
|
mvcEndpointInvokerFactory ?? new MvcEndpointInvokerFactory(new ActionInvokerFactory(Array.Empty<IActionInvokerProvider>())),
|
||||||
|
serviceProvider.GetRequiredService<ParameterPolicyFactory>());
|
||||||
|
|
||||||
return dataSource;
|
return dataSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class UpperCaseParameterTransform : IParameterTransformer
|
||||||
|
{
|
||||||
|
public string Transform(string value)
|
||||||
|
{
|
||||||
|
return value?.ToUpperInvariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private MvcEndpointInfo CreateEndpointInfo(
|
private MvcEndpointInfo CreateEndpointInfo(
|
||||||
string name,
|
string name,
|
||||||
string template,
|
string template,
|
||||||
|
|
@ -768,13 +789,18 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
{
|
{
|
||||||
if (serviceProvider == null)
|
if (serviceProvider == null)
|
||||||
{
|
{
|
||||||
var serviceCollection = new ServiceCollection();
|
var services = new ServiceCollection();
|
||||||
serviceCollection.AddRouting();
|
services.AddRouting();
|
||||||
|
services.AddSingleton(typeof(UpperCaseParameterTransform), new UpperCaseParameterTransform());
|
||||||
|
|
||||||
var routeOptionsSetup = new MvcCoreRouteOptionsSetup();
|
var routeOptionsSetup = new MvcCoreRouteOptionsSetup();
|
||||||
serviceCollection.Configure<RouteOptions>(routeOptionsSetup.Configure);
|
services.Configure<RouteOptions>(routeOptionsSetup.Configure);
|
||||||
|
services.Configure<RouteOptions>(options =>
|
||||||
|
{
|
||||||
|
options.ConstraintMap["upper-case"] = typeof(UpperCaseParameterTransform);
|
||||||
|
});
|
||||||
|
|
||||||
serviceProvider = serviceCollection.BuildServiceProvider();
|
serviceProvider = services.BuildServiceProvider();
|
||||||
}
|
}
|
||||||
|
|
||||||
var parameterPolicyFactory = serviceProvider.GetRequiredService<ParameterPolicyFactory>();
|
var parameterPolicyFactory = serviceProvider.GetRequiredService<ParameterPolicyFactory>();
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,170 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ParameterTransformer_TokenReplacement_Found()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var response = await Client.GetAsync("http://localhost/_ParameterTransformer_/_Test_");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||||
|
|
||||||
|
Assert.Equal("ParameterTransformer", result.Controller);
|
||||||
|
Assert.Equal("Test", result.Action);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ParameterTransformer_TokenReplacement_NotFound()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var response = await Client.GetAsync("http://localhost/ParameterTransformer/Test");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AttributeRoutedAction_Parameters_Found()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var response = await Client.GetAsync("http://localhost/EndpointRouting/Index");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||||
|
|
||||||
|
Assert.Equal("EndpointRouting", result.Controller);
|
||||||
|
Assert.Equal("Index", result.Action);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AttributeRoutedAction_Parameters_DefaultValue_Found()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var response = await Client.GetAsync("http://localhost/EndpointRouting");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||||
|
|
||||||
|
Assert.Equal("EndpointRouting", result.Controller);
|
||||||
|
Assert.Equal("Index", result.Action);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AttributeRoutedAction_ParameterTransformer_Found()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var response = await Client.GetAsync("http://localhost/_EndpointRouting_/ParameterTransformer");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||||
|
|
||||||
|
Assert.Equal("EndpointRouting", result.Controller);
|
||||||
|
Assert.Equal("ParameterTransformer", result.Action);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AttributeRoutedAction_ParameterTransformer_NotFound()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var response = await Client.GetAsync("http://localhost/EndpointRouting/ParameterTransformer");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AttributeRoutedAction_ParameterTransformer_LinkToSelf()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var url = LinkFrom("http://localhost/_EndpointRouting_/ParameterTransformer").To(new { });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await Client.GetAsync(url);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||||
|
|
||||||
|
Assert.Equal("EndpointRouting", result.Controller);
|
||||||
|
Assert.Equal("ParameterTransformer", result.Action);
|
||||||
|
|
||||||
|
Assert.Equal("/_EndpointRouting_/ParameterTransformer", result.Link);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AttributeRoutedAction_ParameterTransformer_LinkWithAmbientController()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var url = LinkFrom("http://localhost/_EndpointRouting_/ParameterTransformer").To(new { action = "Get", id = 5 });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await Client.GetAsync(url);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||||
|
|
||||||
|
Assert.Equal("EndpointRouting", result.Controller);
|
||||||
|
Assert.Equal("ParameterTransformer", result.Action);
|
||||||
|
|
||||||
|
Assert.Equal("/_EndpointRouting_/5", result.Link);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AttributeRoutedAction_ParameterTransformer_LinkToAttributeRoutedController()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var url = LinkFrom("http://localhost/_EndpointRouting_/ParameterTransformer").To(new { action = "ShowPosts", controller = "Blog" });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await Client.GetAsync(url);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||||
|
|
||||||
|
Assert.Equal("EndpointRouting", result.Controller);
|
||||||
|
Assert.Equal("ParameterTransformer", result.Action);
|
||||||
|
|
||||||
|
Assert.Equal("/Blog/ShowPosts", result.Link);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AttributeRoutedAction_ParameterTransformer_LinkToConventionalController()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var url = LinkFrom("http://localhost/_EndpointRouting_/ParameterTransformer").To(new { action = "Index", controller = "Home" });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await Client.GetAsync(url);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||||
|
|
||||||
|
Assert.Equal("EndpointRouting", result.Controller);
|
||||||
|
Assert.Equal("ParameterTransformer", result.Action);
|
||||||
|
|
||||||
|
Assert.Equal("/", result.Link);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async override Task HasEndpointMatch()
|
public async override Task HasEndpointMatch()
|
||||||
{
|
{
|
||||||
|
|
@ -123,5 +287,119 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(HttpStatusCode.MethodNotAllowed, response.StatusCode);
|
Assert.Equal(HttpStatusCode.MethodNotAllowed, response.StatusCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ConventionalRoutedAction_ParameterTransformer()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var response = await Client.GetAsync("http://localhost/ConventionalTransformerRoute/_ConventionalTransformer_/Index");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||||
|
|
||||||
|
Assert.Equal("ConventionalTransformer", result.Controller);
|
||||||
|
Assert.Equal("Index", result.Action);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ConventionalRoutedAction_ParameterTransformer_NotFound()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var response = await Client.GetAsync("http://localhost/ConventionalTransformerRoute/ConventionalTransformer/Index");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ConventionalRoutedAction_ParameterTransformer_DefaultValue()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var response = await Client.GetAsync("http://localhost/ConventionalTransformerRoute/_ConventionalTransformer_");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||||
|
|
||||||
|
Assert.Equal("ConventionalTransformer", result.Controller);
|
||||||
|
Assert.Equal("Index", result.Action);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ConventionalRoutedAction_ParameterTransformer_WithParam()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var response = await Client.GetAsync("http://localhost/ConventionalTransformerRoute/_ConventionalTransformer_/Param/_value_");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||||
|
|
||||||
|
Assert.Equal("ConventionalTransformer", result.Controller);
|
||||||
|
Assert.Equal("Param", result.Action);
|
||||||
|
|
||||||
|
Assert.Equal("/ConventionalTransformerRoute/_ConventionalTransformer_/Param/_value_", Assert.Single(result.ExpectedUrls));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ConventionalRoutedAction_ParameterTransformer_LinkToConventionalController()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var url = LinkFrom("http://localhost/ConventionalTransformerRoute/_ConventionalTransformer_/Index").To(new { action = "Index", controller = "Home" });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await Client.GetAsync(url);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||||
|
|
||||||
|
Assert.Equal("ConventionalTransformer", result.Controller);
|
||||||
|
Assert.Equal("Index", result.Action);
|
||||||
|
Assert.Equal("/", result.Link);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ConventionalRoutedAction_ParameterTransformer_LinkToConventionalControllerWithParam()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var url = LinkFrom("http://localhost/ConventionalTransformerRoute/_ConventionalTransformer_/Index").To(new { action = "Param", controller = "ConventionalTransformer", param = "value" });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await Client.GetAsync(url);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||||
|
|
||||||
|
Assert.Equal("ConventionalTransformer", result.Controller);
|
||||||
|
Assert.Equal("Index", result.Action);
|
||||||
|
Assert.Equal("/ConventionalTransformerRoute/_ConventionalTransformer_/Param/_value_", result.Link);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ConventionalRoutedAction_ParameterTransformer_LinkToSelf()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var url = LinkFrom("http://localhost/ConventionalTransformerRoute/_ConventionalTransformer_/Index").To(new {});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await Client.GetAsync(url);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||||
|
|
||||||
|
Assert.Equal("ConventionalTransformer", result.Controller);
|
||||||
|
Assert.Equal("Index", result.Action);
|
||||||
|
Assert.Equal("/ConventionalTransformerRoute/_ConventionalTransformer_", result.Link);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
|
||||||
|
namespace RoutingWebSite
|
||||||
|
{
|
||||||
|
public class ControllerRouteTokenTransformerConvention : IApplicationModelConvention
|
||||||
|
{
|
||||||
|
private readonly Type _controllerType;
|
||||||
|
private readonly IParameterTransformer _parameterTransformer;
|
||||||
|
|
||||||
|
public ControllerRouteTokenTransformerConvention(Type controllerType, IParameterTransformer parameterTransformer)
|
||||||
|
{
|
||||||
|
if (parameterTransformer == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(parameterTransformer));
|
||||||
|
}
|
||||||
|
|
||||||
|
_controllerType = controllerType;
|
||||||
|
_parameterTransformer = parameterTransformer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Apply(ApplicationModel application)
|
||||||
|
{
|
||||||
|
foreach (var controller in application.Controllers.Where(c => c.ControllerType == _controllerType))
|
||||||
|
{
|
||||||
|
foreach (var action in controller.Actions)
|
||||||
|
{
|
||||||
|
action.Properties[typeof(IParameterTransformer)] = _parameterTransformer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace RoutingWebSite
|
||||||
|
{
|
||||||
|
public class ConventionalTransformerController : Controller
|
||||||
|
{
|
||||||
|
private readonly TestResponseGenerator _generator;
|
||||||
|
|
||||||
|
public ConventionalTransformerController(TestResponseGenerator generator)
|
||||||
|
{
|
||||||
|
_generator = generator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IActionResult Index()
|
||||||
|
{
|
||||||
|
return _generator.Generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IActionResult Param(string param)
|
||||||
|
{
|
||||||
|
return _generator.Generate($"/ConventionalTransformerRoute/_ConventionalTransformer_/Param/{param}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace RoutingWebSite
|
||||||
|
{
|
||||||
|
[Route("/{controller:test-transformer}")]
|
||||||
|
public class EndpointRoutingController : Controller
|
||||||
|
{
|
||||||
|
private readonly TestResponseGenerator _generator;
|
||||||
|
|
||||||
|
public EndpointRoutingController(TestResponseGenerator generator)
|
||||||
|
{
|
||||||
|
_generator = generator;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Route("/{controller}/{action=Index}")]
|
||||||
|
public IActionResult Index()
|
||||||
|
{
|
||||||
|
return _generator.Generate("/EndpointRouting/Index", "/EndpointRouting");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Route("/{controller:test-transformer}/{action}")]
|
||||||
|
public IActionResult ParameterTransformer()
|
||||||
|
{
|
||||||
|
return _generator.Generate("/_EndpointRouting_/ParameterTransformer");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Route("{id}")]
|
||||||
|
public IActionResult Get(int id)
|
||||||
|
{
|
||||||
|
return _generator.Generate("/_EndpointRouting_/" + id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace RoutingWebSite.Controllers
|
namespace RoutingWebSite
|
||||||
{
|
{
|
||||||
[Route("Order/[action]/{orderId?}", Name = "Order_[action]")]
|
[Route("Order/[action]/{orderId?}", Name = "Order_[action]")]
|
||||||
public class OrderController : Controller
|
public class OrderController : Controller
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace RoutingWebSite
|
||||||
|
{
|
||||||
|
[Route("[controller]/[action]", Name = "[controller]_[action]")]
|
||||||
|
public class ParameterTransformerController : Controller
|
||||||
|
{
|
||||||
|
private readonly TestResponseGenerator _generator;
|
||||||
|
|
||||||
|
public ParameterTransformerController(TestResponseGenerator generator)
|
||||||
|
{
|
||||||
|
_generator = generator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IActionResult Test()
|
||||||
|
{
|
||||||
|
return _generator.Generate("/_ParameterTransformer_/_Test_");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,7 +6,7 @@ using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
|
||||||
namespace RoutingWebSite.Controllers
|
namespace RoutingWebSite
|
||||||
{
|
{
|
||||||
public class RouteDataController : Controller
|
public class RouteDataController : Controller
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||||
|
|
||||||
|
namespace RoutingWebSite
|
||||||
|
{
|
||||||
|
public class RemoveControllerActionDescriptorProvider : IActionDescriptorProvider
|
||||||
|
{
|
||||||
|
private readonly Type _controllerType;
|
||||||
|
|
||||||
|
public RemoveControllerActionDescriptorProvider(Type controllerType)
|
||||||
|
{
|
||||||
|
_controllerType = controllerType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Order => int.MaxValue;
|
||||||
|
|
||||||
|
public void OnProvidersExecuted(ActionDescriptorProviderContext context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnProvidersExecuting(ActionDescriptorProviderContext context)
|
||||||
|
{
|
||||||
|
foreach (var item in context.Results.ToList())
|
||||||
|
{
|
||||||
|
if (item is ControllerActionDescriptor controllerActionDescriptor)
|
||||||
|
{
|
||||||
|
if (controllerActionDescriptor.ControllerTypeInfo == _controllerType)
|
||||||
|
{
|
||||||
|
context.Results.Remove(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace RoutingWebSite
|
namespace RoutingWebSite
|
||||||
|
|
@ -14,8 +15,20 @@ namespace RoutingWebSite
|
||||||
// Set up application services
|
// Set up application services
|
||||||
public void ConfigureServices(IServiceCollection services)
|
public void ConfigureServices(IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddMvc()
|
services
|
||||||
|
.AddMvc(options =>
|
||||||
|
{
|
||||||
|
// Add route token transformer to one controller
|
||||||
|
options.Conventions.Add(new ControllerRouteTokenTransformerConvention(
|
||||||
|
typeof(ParameterTransformerController),
|
||||||
|
new TestParameterTransformer()));
|
||||||
|
})
|
||||||
.SetCompatibilityVersion(CompatibilityVersion.Latest);
|
.SetCompatibilityVersion(CompatibilityVersion.Latest);
|
||||||
|
services
|
||||||
|
.AddRouting(options =>
|
||||||
|
{
|
||||||
|
options.ConstraintMap["test-transformer"] = typeof(TestParameterTransformer);
|
||||||
|
});
|
||||||
|
|
||||||
services.AddScoped<TestResponseGenerator>();
|
services.AddScoped<TestResponseGenerator>();
|
||||||
services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
|
services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
|
||||||
|
|
@ -32,6 +45,12 @@ namespace RoutingWebSite
|
||||||
constraints: new { controller = "DataTokens" },
|
constraints: new { controller = "DataTokens" },
|
||||||
dataTokens: new { hasDataTokens = true });
|
dataTokens: new { hasDataTokens = true });
|
||||||
|
|
||||||
|
routes.MapRoute(
|
||||||
|
"ConventionalTransformerRoute",
|
||||||
|
"ConventionalTransformerRoute/{controller:test-transformer}/{action=Index}/{param:test-transformer?}",
|
||||||
|
defaults: null,
|
||||||
|
constraints: new { controller = "ConventionalTransformer" });
|
||||||
|
|
||||||
routes.MapAreaRoute(
|
routes.MapAreaRoute(
|
||||||
"flightRoute",
|
"flightRoute",
|
||||||
"adminRoute",
|
"adminRoute",
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,16 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
|
|
||||||
namespace RoutingWebSite
|
namespace RoutingWebSite
|
||||||
{
|
{
|
||||||
|
|
@ -14,11 +19,17 @@ namespace RoutingWebSite
|
||||||
// Set up application services
|
// Set up application services
|
||||||
public void ConfigureServices(IServiceCollection services)
|
public void ConfigureServices(IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddMvc()
|
services
|
||||||
|
.AddMvc()
|
||||||
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
|
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
|
||||||
|
|
||||||
services.AddScoped<TestResponseGenerator>();
|
services.AddScoped<TestResponseGenerator>();
|
||||||
services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
|
services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
|
||||||
|
|
||||||
|
// EndpointRoutingController is not compatible with old routing
|
||||||
|
// Remove its action to avoid errors
|
||||||
|
var actionDescriptorProvider = new RemoveControllerActionDescriptorProvider(typeof(EndpointRoutingController));
|
||||||
|
services.TryAddEnumerable(ServiceDescriptor.Singleton<IActionDescriptorProvider>(actionDescriptorProvider));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Configure(IApplicationBuilder app)
|
public void Configure(IApplicationBuilder app)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
|
||||||
|
namespace RoutingWebSite
|
||||||
|
{
|
||||||
|
public class TestParameterTransformer : IParameterTransformer
|
||||||
|
{
|
||||||
|
public string Transform(string value)
|
||||||
|
{
|
||||||
|
return "_" + value + "_";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue