[Fixes #3430] Removed RouteKeyHandling.CatchAll
This commit is contained in:
parent
7c40759e32
commit
232b27ad5d
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.24720.0
|
||||
VisualStudioVersion = 14.0.24711.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{DAAE4C74-D06F-4874-A166-33305D2643CE}"
|
||||
EndProject
|
||||
|
|
@ -99,6 +99,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "JsonPatchSample.Web", "samp
|
|||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "LocalizationSample.Web", "samples\LocalizationSample.Web\LocalizationSample.Web.xproj", "{FCFE6024-2720-49B4-8257-9DBC6114F0F1}"
|
||||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ActionConstraintSample.Web", "samples\ActionConstraintSample.Web\ActionConstraintSample.Web.xproj", "{EE0BD773-4D47-4AA8-8472-5A938A3953BA}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
|
@ -591,6 +593,18 @@ Global
|
|||
{FCFE6024-2720-49B4-8257-9DBC6114F0F1}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{FCFE6024-2720-49B4-8257-9DBC6114F0F1}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{FCFE6024-2720-49B4-8257-9DBC6114F0F1}.Release|x86.Build.0 = Release|Any CPU
|
||||
{EE0BD773-4D47-4AA8-8472-5A938A3953BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{EE0BD773-4D47-4AA8-8472-5A938A3953BA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EE0BD773-4D47-4AA8-8472-5A938A3953BA}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{EE0BD773-4D47-4AA8-8472-5A938A3953BA}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{EE0BD773-4D47-4AA8-8472-5A938A3953BA}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{EE0BD773-4D47-4AA8-8472-5A938A3953BA}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{EE0BD773-4D47-4AA8-8472-5A938A3953BA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{EE0BD773-4D47-4AA8-8472-5A938A3953BA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{EE0BD773-4D47-4AA8-8472-5A938A3953BA}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{EE0BD773-4D47-4AA8-8472-5A938A3953BA}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{EE0BD773-4D47-4AA8-8472-5A938A3953BA}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{EE0BD773-4D47-4AA8-8472-5A938A3953BA}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
@ -638,5 +652,6 @@ Global
|
|||
{EA34877F-1AC1-42B7-B4E6-15A093F40CAE} = {DAAE4C74-D06F-4874-A166-33305D2643CE}
|
||||
{DAB1252D-577C-4912-98BE-1A812BF83F86} = {DAAE4C74-D06F-4874-A166-33305D2643CE}
|
||||
{FCFE6024-2720-49B4-8257-9DBC6114F0F1} = {DAAE4C74-D06F-4874-A166-33305D2643CE}
|
||||
{EE0BD773-4D47-4AA8-8472-5A938A3953BA} = {DAAE4C74-D06F-4874-A166-33305D2643CE}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
|
|||
17
Mvc.sln
17
Mvc.sln
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.24720.0
|
||||
VisualStudioVersion = 14.0.24711.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{DAAE4C74-D06F-4874-A166-33305D2643CE}"
|
||||
EndProject
|
||||
|
|
@ -142,6 +142,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "JsonPatchSample.Web", "samp
|
|||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "LocalizationSample.Web", "samples\LocalizationSample.Web\LocalizationSample.Web.xproj", "{FCFE6024-2720-49B4-8257-9DBC6114F0F1}"
|
||||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ActionConstraintSample.Web", "samples\ActionConstraintSample.Web\ActionConstraintSample.Web.xproj", "{EE0BD773-4D47-4AA8-8472-5A938A3953BA}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
|
@ -851,6 +853,18 @@ Global
|
|||
{FCFE6024-2720-49B4-8257-9DBC6114F0F1}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{FCFE6024-2720-49B4-8257-9DBC6114F0F1}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{FCFE6024-2720-49B4-8257-9DBC6114F0F1}.Release|x86.Build.0 = Release|Any CPU
|
||||
{EE0BD773-4D47-4AA8-8472-5A938A3953BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{EE0BD773-4D47-4AA8-8472-5A938A3953BA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EE0BD773-4D47-4AA8-8472-5A938A3953BA}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{EE0BD773-4D47-4AA8-8472-5A938A3953BA}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{EE0BD773-4D47-4AA8-8472-5A938A3953BA}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{EE0BD773-4D47-4AA8-8472-5A938A3953BA}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{EE0BD773-4D47-4AA8-8472-5A938A3953BA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{EE0BD773-4D47-4AA8-8472-5A938A3953BA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{EE0BD773-4D47-4AA8-8472-5A938A3953BA}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{EE0BD773-4D47-4AA8-8472-5A938A3953BA}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{EE0BD773-4D47-4AA8-8472-5A938A3953BA}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{EE0BD773-4D47-4AA8-8472-5A938A3953BA}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
@ -920,5 +934,6 @@ Global
|
|||
{EA34877F-1AC1-42B7-B4E6-15A093F40CAE} = {DAAE4C74-D06F-4874-A166-33305D2643CE}
|
||||
{DAB1252D-577C-4912-98BE-1A812BF83F86} = {DAAE4C74-D06F-4874-A166-33305D2643CE}
|
||||
{FCFE6024-2720-49B4-8257-9DBC6114F0F1} = {DAAE4C74-D06F-4874-A166-33305D2643CE}
|
||||
{EE0BD773-4D47-4AA8-8472-5A938A3953BA} = {DAAE4C74-D06F-4874-A166-33305D2643CE}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>ee0bd773-4d47-4aa8-8472-5a938a3953ba</ProjectGuid>
|
||||
<RootNamespace>ActionConstraintSample.Web</RootNamespace>
|
||||
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
|
||||
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<DnxInvisibleContent Include="bower.json" />
|
||||
<DnxInvisibleContent Include=".bowerrc" />
|
||||
<DnxInvisibleContent Include="package.json" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
</Project>
|
||||
|
|
@ -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.AspNet.Mvc;
|
||||
|
||||
namespace ActionConstraintSample.Web.Controllers
|
||||
{
|
||||
public class ItemsController : Controller
|
||||
{
|
||||
public IActionResult GetItems()
|
||||
{
|
||||
return Content("Hello from everywhere");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// 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.AspNet.Mvc;
|
||||
|
||||
namespace ActionConstraintSample.Web.Controllers.US
|
||||
{
|
||||
[CountrySpecific("US")]
|
||||
public class ItemsController : Controller
|
||||
{
|
||||
public IActionResult GetItems()
|
||||
{
|
||||
return Content("Hello from US");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
// 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.AspNet.Mvc.ActionConstraints;
|
||||
using Microsoft.AspNet.Mvc.Infrastructure;
|
||||
|
||||
namespace ActionConstraintSample.Web
|
||||
{
|
||||
public class CountrySpecificAttribute : RouteConstraintAttribute, IActionConstraint
|
||||
{
|
||||
private readonly string _countryCode;
|
||||
public CountrySpecificAttribute(string countryCode)
|
||||
: base("country", countryCode, blockNonAttributedActions: false)
|
||||
{
|
||||
_countryCode = countryCode;
|
||||
}
|
||||
|
||||
public int Order
|
||||
{
|
||||
get
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Accept(ActionConstraintContext context)
|
||||
{
|
||||
return string.Equals(
|
||||
context.RouteContext.RouteData.Values["country"].ToString(),
|
||||
_countryCode,
|
||||
StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
// 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.AspNet.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace ActionConstraintSample.Web
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
// Set up application services
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddMvc();
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app)
|
||||
{
|
||||
app.UseMvc(routes =>
|
||||
{
|
||||
routes.MapRoute(
|
||||
"items",
|
||||
"Items/{country}/{action}",
|
||||
defaults: new { controller = "Items" });
|
||||
routes.MapRoute("default", "{controller}/{action}");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"commands": {
|
||||
"web": "Microsoft.AspNet.Server.Kestrel",
|
||||
"weblistener": "Microsoft.AspNet.Server.WebListener"
|
||||
},
|
||||
"dependencies": {
|
||||
"Microsoft.AspNet.Mvc": "6.0.0-*",
|
||||
"Microsoft.AspNet.Server.Kestrel": "1.0.0-*",
|
||||
"Microsoft.AspNet.Server.WebListener": "1.0.0-*"
|
||||
},
|
||||
"frameworks": {
|
||||
"dnx451": { },
|
||||
"dnxcore50": { }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
ActionConstraintSample.Web
|
||||
===
|
||||
|
||||
This web site illustrates how to setup multiple controllers/actions with same name that is selected based on specific route values using IActionConstraint.
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<system.webServer>
|
||||
<handlers>
|
||||
<add name="httpPlatformHandler" path="*" verb="*" modules="httpPlatformHandler" resourceType="Unspecified"/>
|
||||
</handlers>
|
||||
<httpPlatform processPath="%DNX_PATH%" arguments="%DNX_ARGS%" stdoutLogEnabled="false" startupTimeLimit="3600"/>
|
||||
</system.webServer>
|
||||
</configuration>
|
||||
|
|
@ -45,20 +45,6 @@ namespace Microsoft.AspNet.Mvc.Routing
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a catch all constraint for the given key.
|
||||
/// </summary>
|
||||
/// <param name="routeKey">Route key.</param>
|
||||
/// <returns>a <see cref="RouteDataActionConstraint"/> that represents a catch all constraint.</returns>
|
||||
public static RouteDataActionConstraint CreateCatchAll(string routeKey)
|
||||
{
|
||||
var c = new RouteDataActionConstraint(routeKey);
|
||||
c.KeyHandling = RouteKeyHandling.CatchAll;
|
||||
c.RouteValue = string.Empty;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The route key this constraint matches against.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -14,12 +14,5 @@ namespace Microsoft.AspNet.Mvc.Routing
|
|||
/// Requires that the key will not be in the route values.
|
||||
/// </summary>
|
||||
DenyKey,
|
||||
|
||||
/// <summary>
|
||||
/// Requires that the key will be in the route values, but ignore the content.
|
||||
/// Constraints with this value are considered less important than ones with
|
||||
/// <see cref="RequireKey"/>
|
||||
/// </summary>
|
||||
CatchAll,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -464,18 +464,9 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
|||
// Skip duplicates
|
||||
if (!HasConstraint(actionDescriptor.RouteConstraints, constraintAttribute.RouteKey))
|
||||
{
|
||||
if (constraintAttribute.RouteKeyHandling == RouteKeyHandling.CatchAll)
|
||||
{
|
||||
actionDescriptor.RouteConstraints.Add(
|
||||
RouteDataActionConstraint.CreateCatchAll(
|
||||
constraintAttribute.RouteKey));
|
||||
}
|
||||
else
|
||||
{
|
||||
actionDescriptor.RouteConstraints.Add(new RouteDataActionConstraint(
|
||||
constraintAttribute.RouteKey,
|
||||
constraintAttribute.RouteValue));
|
||||
}
|
||||
actionDescriptor.RouteConstraints.Add(new RouteDataActionConstraint(
|
||||
constraintAttribute.RouteKey,
|
||||
constraintAttribute.RouteValue));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -489,18 +480,9 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
|||
// Skip duplicates - this also means that a value on the action will take precedence
|
||||
if (!HasConstraint(actionDescriptor.RouteConstraints, constraintAttribute.RouteKey))
|
||||
{
|
||||
if (constraintAttribute.RouteKeyHandling == RouteKeyHandling.CatchAll)
|
||||
{
|
||||
actionDescriptor.RouteConstraints.Add(
|
||||
RouteDataActionConstraint.CreateCatchAll(
|
||||
constraintAttribute.RouteKey));
|
||||
}
|
||||
else
|
||||
{
|
||||
actionDescriptor.RouteConstraints.Add(new RouteDataActionConstraint(
|
||||
constraintAttribute.RouteKey,
|
||||
constraintAttribute.RouteValue));
|
||||
}
|
||||
actionDescriptor.RouteConstraints.Add(new RouteDataActionConstraint(
|
||||
constraintAttribute.RouteKey,
|
||||
constraintAttribute.RouteValue));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,16 +21,11 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
|
|||
public abstract class RouteConstraintAttribute : Attribute, IRouteConstraintProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="RouteConstraintAttribute"/>.
|
||||
/// Creates a new <see cref="RouteConstraintAttribute"/> with <see cref="RouteKeyHandling"/> set as
|
||||
/// <see cref="RouteKeyHandling.DenyKey"/>.
|
||||
/// </summary>
|
||||
/// <param name="routeKey">The route value key.</param>
|
||||
/// <param name="keyHandling">
|
||||
/// The <see cref="RouteKeyHandling"/> value. Must be <see cref="RouteKeyHandling.CatchAll "/>
|
||||
/// or <see cref="RouteKeyHandling.DenyKey"/>.
|
||||
/// </param>
|
||||
protected RouteConstraintAttribute(
|
||||
string routeKey,
|
||||
RouteKeyHandling keyHandling)
|
||||
protected RouteConstraintAttribute(string routeKey)
|
||||
{
|
||||
if (routeKey == null)
|
||||
{
|
||||
|
|
@ -38,16 +33,7 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
|
|||
}
|
||||
|
||||
RouteKey = routeKey;
|
||||
RouteKeyHandling = keyHandling;
|
||||
|
||||
if (keyHandling != RouteKeyHandling.CatchAll &&
|
||||
keyHandling != RouteKeyHandling.DenyKey)
|
||||
{
|
||||
var message = Resources.FormatRouteConstraintAttribute_InvalidKeyHandlingValue(
|
||||
Enum.GetName(typeof(RouteKeyHandling), RouteKeyHandling.CatchAll),
|
||||
Enum.GetName(typeof(RouteKeyHandling), RouteKeyHandling.DenyKey));
|
||||
throw new ArgumentException(message, nameof(keyHandling));
|
||||
}
|
||||
RouteKeyHandling = RouteKeyHandling.DenyKey;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -474,22 +474,6 @@ namespace Microsoft.AspNet.Mvc.Core
|
|||
return GetString("AttributeRoute_TokenReplacement_UnescapedBraceInToken");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The value must be either '{0}' or '{1}'.
|
||||
/// </summary>
|
||||
internal static string RouteConstraintAttribute_InvalidKeyHandlingValue
|
||||
{
|
||||
get { return GetString("RouteConstraintAttribute_InvalidKeyHandlingValue"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The value must be either '{0}' or '{1}'.
|
||||
/// </summary>
|
||||
internal static string FormatRouteConstraintAttribute_InvalidKeyHandlingValue(object p0, object p1)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("RouteConstraintAttribute_InvalidKeyHandlingValue"), p0, p1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unable to find the required services. Please add all the required services by calling '{0}' inside the call to '{1}' or '{2}' in the application startup code.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -207,9 +207,6 @@
|
|||
<data name="AttributeRoute_TokenReplacement_UnescapedBraceInToken" xml:space="preserve">
|
||||
<value>An unescaped '[' token is not allowed inside of a replacement token. Use '[[' to escape.</value>
|
||||
</data>
|
||||
<data name="RouteConstraintAttribute_InvalidKeyHandlingValue" xml:space="preserve">
|
||||
<value>The value must be either '{0}' or '{1}'.</value>
|
||||
</data>
|
||||
<data name="UnableToFindServices" xml:space="preserve">
|
||||
<value>Unable to find the required services. Please add all the required services by calling '{0}' inside the call to '{1}' or '{2}' in the application startup code.</value>
|
||||
</data>
|
||||
|
|
|
|||
|
|
@ -41,69 +41,7 @@ namespace Microsoft.AspNet.Mvc.Routing
|
|||
var results = new List<ActionDescriptor>();
|
||||
Walk(results, routeValues, _root);
|
||||
|
||||
// If we have a match that isn't using catch-all, then it's considered better than matches with catch all
|
||||
// so filter those out.
|
||||
var hasNonCatchAll = false;
|
||||
|
||||
// The common case for MVC has no catch-alls, so avoid allocating.
|
||||
List<ActionDescriptor> filtered = null;
|
||||
|
||||
// Perf: Avoid allocations
|
||||
for (var i = 0; i < results.Count; i++)
|
||||
{
|
||||
var action = results[i];
|
||||
|
||||
var actionHasCatchAll = false;
|
||||
if (action.RouteConstraints != null)
|
||||
{
|
||||
for (var j = 0; j < action.RouteConstraints.Count; j++)
|
||||
{
|
||||
var constraint = action.RouteConstraints[j];
|
||||
if (constraint.KeyHandling == RouteKeyHandling.CatchAll)
|
||||
{
|
||||
actionHasCatchAll = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasNonCatchAll && actionHasCatchAll)
|
||||
{
|
||||
// Do nothing - we've already found a better match.
|
||||
}
|
||||
else if (actionHasCatchAll)
|
||||
{
|
||||
if (filtered == null)
|
||||
{
|
||||
filtered = new List<ActionDescriptor>();
|
||||
}
|
||||
|
||||
filtered.Add(action);
|
||||
}
|
||||
else if (hasNonCatchAll)
|
||||
{
|
||||
Debug.Assert(filtered != null);
|
||||
filtered.Add(action);
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is the first non-catch-all we've found.
|
||||
hasNonCatchAll = true;
|
||||
|
||||
if (filtered == null)
|
||||
{
|
||||
filtered = new List<ActionDescriptor>();
|
||||
}
|
||||
else
|
||||
{
|
||||
filtered.Clear();
|
||||
}
|
||||
|
||||
filtered.Add(action);
|
||||
}
|
||||
}
|
||||
|
||||
return filtered ?? results;
|
||||
return results;
|
||||
}
|
||||
|
||||
private void Walk(
|
||||
|
|
@ -122,20 +60,13 @@ namespace Microsoft.AspNet.Mvc.Routing
|
|||
var key = criterion.Key;
|
||||
|
||||
object value;
|
||||
var hasValue = routeValues.TryGetValue(key, out value);
|
||||
routeValues.TryGetValue(key, out value);
|
||||
|
||||
DecisionTreeNode<ActionDescriptor> branch;
|
||||
if (criterion.Branches.TryGetValue(value ?? string.Empty, out branch))
|
||||
{
|
||||
Walk(results, routeValues, branch);
|
||||
}
|
||||
|
||||
// If there's a fallback node we always need to process it when we have a value. We'll prioritize
|
||||
// non-fallback matches later in the process.
|
||||
if (hasValue && criterion.Fallback != null)
|
||||
{
|
||||
Walk(results, routeValues, criterion.Fallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -157,19 +88,15 @@ namespace Microsoft.AspNet.Mvc.Routing
|
|||
foreach (var constraint in item.RouteConstraints)
|
||||
{
|
||||
DecisionCriterionValue value;
|
||||
if (constraint.KeyHandling == RouteKeyHandling.CatchAll)
|
||||
{
|
||||
value = new DecisionCriterionValue(value: null, isCatchAll: true);
|
||||
}
|
||||
else if (constraint.KeyHandling == RouteKeyHandling.DenyKey)
|
||||
if (constraint.KeyHandling == RouteKeyHandling.DenyKey)
|
||||
{
|
||||
// null and string.Empty are equivalent for route values, so just treat nulls as
|
||||
// string.Empty.
|
||||
value = new DecisionCriterionValue(value: string.Empty, isCatchAll: false);
|
||||
value = new DecisionCriterionValue(value: string.Empty);
|
||||
}
|
||||
else if (constraint.KeyHandling == RouteKeyHandling.RequireKey)
|
||||
{
|
||||
value = new DecisionCriterionValue(value: constraint.RouteValue, isCatchAll: false);
|
||||
value = new DecisionCriterionValue(value: constraint.RouteValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
{
|
||||
return null;
|
||||
}
|
||||
else if (constraint.KeyHandling == RouteKeyHandling.RequireKey)
|
||||
else
|
||||
{
|
||||
normalizedValue = constraint.RouteValue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -347,92 +347,6 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
|
|||
Assert.Same(action, best);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SelectAsync_WithCatchAll_PrefersNonCatchAll()
|
||||
{
|
||||
// Arrange
|
||||
var actions = new ActionDescriptor[]
|
||||
{
|
||||
CreateAction(area: null, controller: "Store", action: "Buy"),
|
||||
CreateAction(area: null, controller: "Store", action: "Buy"),
|
||||
CreateAction(area: null, controller: "Store", action: "Buy"),
|
||||
};
|
||||
|
||||
actions[0].RouteConstraints.Add(new RouteDataActionConstraint("country", "CA"));
|
||||
actions[1].RouteConstraints.Add(new RouteDataActionConstraint("country", "US"));
|
||||
actions[2].RouteConstraints.Add(RouteDataActionConstraint.CreateCatchAll("country"));
|
||||
|
||||
var selector = CreateSelector(actions);
|
||||
var context = CreateRouteContext("GET");
|
||||
|
||||
context.RouteData.Values.Add("controller", "Store");
|
||||
context.RouteData.Values.Add("action", "Buy");
|
||||
context.RouteData.Values.Add("country", "CA");
|
||||
|
||||
// Act
|
||||
var action = await selector.SelectAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.Same(action, actions[0]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SelectAsync_WithCatchAll_CatchAllIsOnlyMatch()
|
||||
{
|
||||
// Arrange
|
||||
var actions = new ActionDescriptor[]
|
||||
{
|
||||
CreateAction(area: null, controller: "Store", action: "Buy"),
|
||||
CreateAction(area: null, controller: "Store", action: "Buy"),
|
||||
CreateAction(area: null, controller: "Store", action: "Buy"),
|
||||
};
|
||||
|
||||
actions[0].RouteConstraints.Add(new RouteDataActionConstraint("country", "CA"));
|
||||
actions[1].RouteConstraints.Add(new RouteDataActionConstraint("country", "US"));
|
||||
actions[2].RouteConstraints.Add(RouteDataActionConstraint.CreateCatchAll("country"));
|
||||
|
||||
var selector = CreateSelector(actions);
|
||||
var context = CreateRouteContext("GET");
|
||||
|
||||
context.RouteData.Values.Add("controller", "Store");
|
||||
context.RouteData.Values.Add("action", "Buy");
|
||||
context.RouteData.Values.Add("country", "DE");
|
||||
|
||||
// Act
|
||||
var action = await selector.SelectAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.Same(action, actions[2]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SelectAsync_WithCatchAll_NoMatch()
|
||||
{
|
||||
// Arrange
|
||||
var actions = new ActionDescriptor[]
|
||||
{
|
||||
CreateAction(area: null, controller: "Store", action: "Buy"),
|
||||
CreateAction(area: null, controller: "Store", action: "Buy"),
|
||||
CreateAction(area: null, controller: "Store", action: "Buy"),
|
||||
};
|
||||
|
||||
actions[0].RouteConstraints.Add(new RouteDataActionConstraint("country", "CA"));
|
||||
actions[1].RouteConstraints.Add(new RouteDataActionConstraint("country", "US"));
|
||||
actions[2].RouteConstraints.Add(RouteDataActionConstraint.CreateCatchAll("country"));
|
||||
|
||||
var selector = CreateSelector(actions);
|
||||
var context = CreateRouteContext("GET");
|
||||
|
||||
context.RouteData.Values.Add("controller", "Store");
|
||||
context.RouteData.Values.Add("action", "Buy");
|
||||
|
||||
// Act
|
||||
var action = await selector.SelectAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.Null(action);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SelectAsync_Ambiguous()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -37,15 +37,5 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
|
|||
Assert.Equal(routeDataConstraint.KeyHandling, RouteKeyHandling.RequireKey);
|
||||
Assert.Equal(routeDataConstraint.RouteValue, "value");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RouteDataActionConstraint_CatchAll()
|
||||
{
|
||||
var routeDataConstraint = RouteDataActionConstraint.CreateCatchAll("key");
|
||||
|
||||
Assert.Equal(routeDataConstraint.RouteKey, "key");
|
||||
Assert.Equal(routeDataConstraint.KeyHandling, RouteKeyHandling.CatchAll);
|
||||
Assert.Equal(routeDataConstraint.RouteValue, string.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
// 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.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||
{
|
||||
public class ActionConstraintSampleTest : IClassFixture<MvcTestFixture<ActionConstraintSample.Web.Startup>>
|
||||
{
|
||||
public ActionConstraintSampleTest(MvcTestFixture<ActionConstraintSample.Web.Startup> fixture)
|
||||
{
|
||||
Client = fixture.Client;
|
||||
}
|
||||
|
||||
public HttpClient Client { get; }
|
||||
|
||||
[Fact]
|
||||
public async Task ControllerWithActionConstraint_SelectsSpecificController()
|
||||
{
|
||||
// Arrange
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/Items/US/GetItems");
|
||||
request.Headers.Add("User", "Blah");
|
||||
|
||||
// Act
|
||||
var response = await Client.SendAsync(request);
|
||||
|
||||
// Assert
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
Assert.Equal("Hello from US", body);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ControllerWithActionConstraint_NoMatchesFound_SelectsDefaultController()
|
||||
{
|
||||
// Arrange
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/Items/CA/GetItems");
|
||||
request.Headers.Add("User", "Blah");
|
||||
|
||||
// Act
|
||||
var response = await Client.SendAsync(request);
|
||||
|
||||
// Assert
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
Assert.Equal("Hello from everywhere", body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1089,90 +1089,6 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
|||
Assert.Equal("/Admin/Users/All", result.Link);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ControllerWithCatchAll_CanReachSpecificCountry()
|
||||
{
|
||||
// Arrange & Act
|
||||
var response = await Client.GetAsync("http://localhost/api/Products/US/GetProducts");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||
|
||||
Assert.Contains("/api/Products/US/GetProducts", result.ExpectedUrls);
|
||||
Assert.Equal("Products", result.Controller);
|
||||
Assert.Equal("GetProducts", result.Action);
|
||||
Assert.Equal(
|
||||
new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
{ "country", "US" },
|
||||
{ "action", "GetProducts" },
|
||||
{ "controller", "Products" },
|
||||
},
|
||||
result.RouteValues);
|
||||
}
|
||||
|
||||
// The 'default' route doesn't provide a value for {country}
|
||||
[Fact]
|
||||
public async Task ControllerWithCatchAll_CannotReachWithoutCountry()
|
||||
{
|
||||
// Arrange & Act
|
||||
var response = await Client.GetAsync("http://localhost/Products/GetProducts");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ControllerWithCatchAll_GenerateLinkForSpecificCountry()
|
||||
{
|
||||
// Arrange
|
||||
var url = LinkFrom("http://localhost/")
|
||||
.To(new { action = "GetProducts", controller = "Products", country = "US" });
|
||||
|
||||
// Act
|
||||
var response = await Client.GetAsync(url);
|
||||
|
||||
// Assert
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||
Assert.Equal("/api/Products/US/GetProducts", result.Link);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ControllerWithCatchAll_GenerateLinkForFallback()
|
||||
{
|
||||
// Arrange
|
||||
var url = LinkFrom("http://localhost/")
|
||||
.To(new { action = "GetProducts", controller = "Products", country = "CA" });
|
||||
|
||||
// Act
|
||||
var response = await Client.GetAsync(url);
|
||||
|
||||
// Assert
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||
Assert.Equal("/api/Products/CA/GetProducts", result.Link);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ControllerWithCatchAll_GenerateLink_FallsThroughWithoutCountry()
|
||||
{
|
||||
// Arrange
|
||||
var url = LinkFrom("http://localhost/")
|
||||
.To(new { action = "GetProducts", controller = "Products", country = (string)null });
|
||||
|
||||
// Act
|
||||
var response = await Client.GetAsync(url);
|
||||
|
||||
// Assert
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||
Assert.Equal("/Products/GetProducts", result.Link);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("/Bank/Deposit", "PUT", "Deposit")]
|
||||
[InlineData("/Bank/Deposit", "POST", "Deposit")]
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
"warningsAsErrors": true
|
||||
},
|
||||
"dependencies": {
|
||||
"ActionConstraintSample.Web": "1.0.0",
|
||||
"ApiExplorerWebSite": "1.0.0",
|
||||
"ApplicationModelWebSite": "1.0.0",
|
||||
"BasicWebSite": "1.0.0",
|
||||
|
|
|
|||
|
|
@ -1577,34 +1577,6 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetNormalizedRouteValue_ReturnsRouteDataValue_IfRouteConstraintKeyHandlingIsCatchAll()
|
||||
{
|
||||
// Arrange
|
||||
var key = "some-key";
|
||||
var actionDescriptor = new ActionDescriptor
|
||||
{
|
||||
RouteConstraints = new[]
|
||||
{
|
||||
RouteDataActionConstraint.CreateCatchAll(key)
|
||||
}
|
||||
};
|
||||
|
||||
var actionContext = new ActionContext
|
||||
{
|
||||
ActionDescriptor = actionDescriptor,
|
||||
RouteData = new RouteData()
|
||||
};
|
||||
|
||||
actionContext.RouteData.Values[key] = "route-value";
|
||||
|
||||
// Act
|
||||
var result = RazorViewEngine.GetNormalizedRouteValue(actionContext, key);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("route-value", result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetNormalizedRouteValue_UsesRouteValueDefaults_IfAttributeRouted()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,23 +0,0 @@
|
|||
// 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.AspNet.Mvc;
|
||||
|
||||
namespace RoutingWebSite.Products
|
||||
{
|
||||
[CountryNeutral]
|
||||
public class ProductsController : Controller
|
||||
{
|
||||
private readonly TestResponseGenerator _generator;
|
||||
|
||||
public ProductsController(TestResponseGenerator generator)
|
||||
{
|
||||
_generator = generator;
|
||||
}
|
||||
|
||||
public IActionResult GetProducts()
|
||||
{
|
||||
return _generator.Generate("/api/Products/CA/GetProducts");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
// 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.AspNet.Mvc;
|
||||
|
||||
namespace RoutingWebSite.Products.US
|
||||
{
|
||||
[CountrySpecific("US")]
|
||||
public class ProductsController : Controller
|
||||
{
|
||||
private readonly TestResponseGenerator _generator;
|
||||
|
||||
public ProductsController(TestResponseGenerator generator)
|
||||
{
|
||||
_generator = generator;
|
||||
}
|
||||
|
||||
public IActionResult GetProducts()
|
||||
{
|
||||
return _generator.Generate("/api/Products/US/GetProducts");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
// 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.AspNet.Mvc.Infrastructure;
|
||||
using Microsoft.AspNet.Mvc.Routing;
|
||||
|
||||
namespace RoutingWebSite
|
||||
{
|
||||
public class CountryNeutralAttribute : RouteConstraintAttribute
|
||||
{
|
||||
public CountryNeutralAttribute()
|
||||
: base("country", RouteKeyHandling.CatchAll)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
// 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.AspNet.Mvc.Infrastructure;
|
||||
|
||||
namespace RoutingWebSite
|
||||
{
|
||||
public class CountrySpecificAttribute : RouteConstraintAttribute
|
||||
{
|
||||
public CountrySpecificAttribute(string countryCode)
|
||||
: base("country", countryCode, blockNonAttributedActions: false)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -31,11 +31,6 @@ namespace RoutingWebSite
|
|||
"{area:exists}/{controller}/{action}",
|
||||
new { controller = "Home", action = "Index" });
|
||||
|
||||
routes.MapRoute(
|
||||
"products",
|
||||
"api/Products/{country}/{action}",
|
||||
defaults: new { controller = "Products" });
|
||||
|
||||
routes.MapRoute(
|
||||
"ActionAsMethod",
|
||||
"{controller}/{action}",
|
||||
|
|
|
|||
Loading…
Reference in New Issue