diff --git a/Mvc.NoFun.sln b/Mvc.NoFun.sln
index 2497517719..d5b452ae82 100644
--- a/Mvc.NoFun.sln
+++ b/Mvc.NoFun.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
@@ -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
diff --git a/Mvc.sln b/Mvc.sln
index 611eb28d1a..9ab0f304cb 100644
--- a/Mvc.sln
+++ b/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
diff --git a/samples/ActionConstraintSample.Web/ActionConstraintSample.Web.xproj b/samples/ActionConstraintSample.Web/ActionConstraintSample.Web.xproj
new file mode 100644
index 0000000000..8d03bc7958
--- /dev/null
+++ b/samples/ActionConstraintSample.Web/ActionConstraintSample.Web.xproj
@@ -0,0 +1,25 @@
+
+
+
+ 14.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+
+
+
+
+ ee0bd773-4d47-4aa8-8472-5a938a3953ba
+ ActionConstraintSample.Web
+ ..\..\artifacts\obj\$(MSBuildProjectName)
+ ..\..\artifacts\bin\$(MSBuildProjectName)\
+
+
+
+ 2.0
+
+
+
+
+
+
+
+
diff --git a/samples/ActionConstraintSample.Web/Controllers/ItemsController.cs b/samples/ActionConstraintSample.Web/Controllers/ItemsController.cs
new file mode 100644
index 0000000000..5a1eec61cd
--- /dev/null
+++ b/samples/ActionConstraintSample.Web/Controllers/ItemsController.cs
@@ -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");
+ }
+ }
+}
\ No newline at end of file
diff --git a/samples/ActionConstraintSample.Web/Controllers/US/ItemsController.cs b/samples/ActionConstraintSample.Web/Controllers/US/ItemsController.cs
new file mode 100644
index 0000000000..7115344753
--- /dev/null
+++ b/samples/ActionConstraintSample.Web/Controllers/US/ItemsController.cs
@@ -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");
+ }
+ }
+}
\ No newline at end of file
diff --git a/samples/ActionConstraintSample.Web/CountrySpecificAttribute.cs b/samples/ActionConstraintSample.Web/CountrySpecificAttribute.cs
new file mode 100644
index 0000000000..17cfc5ed7a
--- /dev/null
+++ b/samples/ActionConstraintSample.Web/CountrySpecificAttribute.cs
@@ -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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/samples/ActionConstraintSample.Web/Startup.cs b/samples/ActionConstraintSample.Web/Startup.cs
new file mode 100644
index 0000000000..8d5a8a5a2e
--- /dev/null
+++ b/samples/ActionConstraintSample.Web/Startup.cs
@@ -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}");
+ });
+ }
+ }
+}
diff --git a/samples/ActionConstraintSample.Web/project.json b/samples/ActionConstraintSample.Web/project.json
new file mode 100644
index 0000000000..fd0f9b0fc3
--- /dev/null
+++ b/samples/ActionConstraintSample.Web/project.json
@@ -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": { }
+ }
+}
\ No newline at end of file
diff --git a/samples/ActionConstraintSample.Web/readme.md b/samples/ActionConstraintSample.Web/readme.md
new file mode 100644
index 0000000000..b469272c44
--- /dev/null
+++ b/samples/ActionConstraintSample.Web/readme.md
@@ -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.
diff --git a/samples/ActionConstraintSample.Web/wwwroot/web.config b/samples/ActionConstraintSample.Web/wwwroot/web.config
new file mode 100644
index 0000000000..8485f6719f
--- /dev/null
+++ b/samples/ActionConstraintSample.Web/wwwroot/web.config
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/Microsoft.AspNet.Mvc.Abstractions/Routing/RouteDataActionConstraint.cs b/src/Microsoft.AspNet.Mvc.Abstractions/Routing/RouteDataActionConstraint.cs
index 1244ab42c4..65e22c0de9 100644
--- a/src/Microsoft.AspNet.Mvc.Abstractions/Routing/RouteDataActionConstraint.cs
+++ b/src/Microsoft.AspNet.Mvc.Abstractions/Routing/RouteDataActionConstraint.cs
@@ -45,20 +45,6 @@ namespace Microsoft.AspNet.Mvc.Routing
}
}
- ///
- /// Create a catch all constraint for the given key.
- ///
- /// Route key.
- /// a that represents a catch all constraint.
- public static RouteDataActionConstraint CreateCatchAll(string routeKey)
- {
- var c = new RouteDataActionConstraint(routeKey);
- c.KeyHandling = RouteKeyHandling.CatchAll;
- c.RouteValue = string.Empty;
-
- return c;
- }
-
///
/// The route key this constraint matches against.
///
diff --git a/src/Microsoft.AspNet.Mvc.Abstractions/Routing/RouteKeyHandling.cs b/src/Microsoft.AspNet.Mvc.Abstractions/Routing/RouteKeyHandling.cs
index aee03ff0e4..21675a11bb 100644
--- a/src/Microsoft.AspNet.Mvc.Abstractions/Routing/RouteKeyHandling.cs
+++ b/src/Microsoft.AspNet.Mvc.Abstractions/Routing/RouteKeyHandling.cs
@@ -14,12 +14,5 @@ namespace Microsoft.AspNet.Mvc.Routing
/// Requires that the key will not be in the route values.
///
DenyKey,
-
- ///
- /// 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
- ///
- ///
- CatchAll,
}
}
diff --git a/src/Microsoft.AspNet.Mvc.Core/Controllers/ControllerActionDescriptorBuilder.cs b/src/Microsoft.AspNet.Mvc.Core/Controllers/ControllerActionDescriptorBuilder.cs
index a2dfa778f5..49852f9a5b 100644
--- a/src/Microsoft.AspNet.Mvc.Core/Controllers/ControllerActionDescriptorBuilder.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/Controllers/ControllerActionDescriptorBuilder.cs
@@ -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));
}
}
diff --git a/src/Microsoft.AspNet.Mvc.Core/Infrastructure/RouteConstraintAttribute.cs b/src/Microsoft.AspNet.Mvc.Core/Infrastructure/RouteConstraintAttribute.cs
index 0f31c57d24..d2d48d84d2 100644
--- a/src/Microsoft.AspNet.Mvc.Core/Infrastructure/RouteConstraintAttribute.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/Infrastructure/RouteConstraintAttribute.cs
@@ -21,16 +21,11 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
public abstract class RouteConstraintAttribute : Attribute, IRouteConstraintProvider
{
///
- /// Creates a new .
+ /// Creates a new with set as
+ /// .
///
/// The route value key.
- ///
- /// The value. Must be
- /// or .
- ///
- 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;
}
///
diff --git a/src/Microsoft.AspNet.Mvc.Core/Properties/Resources.Designer.cs b/src/Microsoft.AspNet.Mvc.Core/Properties/Resources.Designer.cs
index 777e416d2c..be8aff9c5b 100644
--- a/src/Microsoft.AspNet.Mvc.Core/Properties/Resources.Designer.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/Properties/Resources.Designer.cs
@@ -474,22 +474,6 @@ namespace Microsoft.AspNet.Mvc.Core
return GetString("AttributeRoute_TokenReplacement_UnescapedBraceInToken");
}
- ///
- /// The value must be either '{0}' or '{1}'.
- ///
- internal static string RouteConstraintAttribute_InvalidKeyHandlingValue
- {
- get { return GetString("RouteConstraintAttribute_InvalidKeyHandlingValue"); }
- }
-
- ///
- /// The value must be either '{0}' or '{1}'.
- ///
- internal static string FormatRouteConstraintAttribute_InvalidKeyHandlingValue(object p0, object p1)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("RouteConstraintAttribute_InvalidKeyHandlingValue"), p0, p1);
- }
-
///
/// 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.
///
diff --git a/src/Microsoft.AspNet.Mvc.Core/Resources.resx b/src/Microsoft.AspNet.Mvc.Core/Resources.resx
index 5a0b351d07..45e61ee6b4 100644
--- a/src/Microsoft.AspNet.Mvc.Core/Resources.resx
+++ b/src/Microsoft.AspNet.Mvc.Core/Resources.resx
@@ -207,9 +207,6 @@
An unescaped '[' token is not allowed inside of a replacement token. Use '[[' to escape.
-
- The value must be either '{0}' or '{1}'.
-
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.
diff --git a/src/Microsoft.AspNet.Mvc.Core/Routing/ActionSelectionDecisionTree.cs b/src/Microsoft.AspNet.Mvc.Core/Routing/ActionSelectionDecisionTree.cs
index 1faac404d2..80ce81af5d 100644
--- a/src/Microsoft.AspNet.Mvc.Core/Routing/ActionSelectionDecisionTree.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/Routing/ActionSelectionDecisionTree.cs
@@ -41,69 +41,7 @@ namespace Microsoft.AspNet.Mvc.Routing
var results = new List();
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 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();
- }
-
- 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();
- }
- 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 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
{
diff --git a/src/Microsoft.AspNet.Mvc.Razor/RazorViewEngine.cs b/src/Microsoft.AspNet.Mvc.Razor/RazorViewEngine.cs
index 202030b839..4627cc3e3a 100644
--- a/src/Microsoft.AspNet.Mvc.Razor/RazorViewEngine.cs
+++ b/src/Microsoft.AspNet.Mvc.Razor/RazorViewEngine.cs
@@ -154,7 +154,7 @@ namespace Microsoft.AspNet.Mvc.Razor
{
return null;
}
- else if (constraint.KeyHandling == RouteKeyHandling.RequireKey)
+ else
{
normalizedValue = constraint.RouteValue;
}
diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Infrastructure/DefaultActionSelectorTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Infrastructure/DefaultActionSelectorTests.cs
index f424b059dd..a4221d16c4 100644
--- a/test/Microsoft.AspNet.Mvc.Core.Test/Infrastructure/DefaultActionSelectorTests.cs
+++ b/test/Microsoft.AspNet.Mvc.Core.Test/Infrastructure/DefaultActionSelectorTests.cs
@@ -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()
{
diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Infrastructure/RouteDataActionConstraintTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Infrastructure/RouteDataActionConstraintTest.cs
index 818c841c5b..c75173b8f2 100644
--- a/test/Microsoft.AspNet.Mvc.Core.Test/Infrastructure/RouteDataActionConstraintTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Core.Test/Infrastructure/RouteDataActionConstraintTest.cs
@@ -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);
- }
}
}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/ActionConstraintSampleTest.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/ActionConstraintSampleTest.cs
new file mode 100644
index 0000000000..e2d72b329e
--- /dev/null
+++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/ActionConstraintSampleTest.cs
@@ -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>
+ {
+ public ActionConstraintSampleTest(MvcTestFixture 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);
+ }
+ }
+}
diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/RoutingTests.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/RoutingTests.cs
index 22403d710a..d30132a12a 100644
--- a/test/Microsoft.AspNet.Mvc.FunctionalTests/RoutingTests.cs
+++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/RoutingTests.cs
@@ -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(body);
-
- Assert.Contains("/api/Products/US/GetProducts", result.ExpectedUrls);
- Assert.Equal("Products", result.Controller);
- Assert.Equal("GetProducts", result.Action);
- Assert.Equal(
- new Dictionary(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(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(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(body);
- Assert.Equal("/Products/GetProducts", result.Link);
- }
-
[Theory]
[InlineData("/Bank/Deposit", "PUT", "Deposit")]
[InlineData("/Bank/Deposit", "POST", "Deposit")]
diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json b/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json
index 2ed994214c..9cd18e4a4b 100644
--- a/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json
+++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json
@@ -7,6 +7,7 @@
"warningsAsErrors": true
},
"dependencies": {
+ "ActionConstraintSample.Web": "1.0.0",
"ApiExplorerWebSite": "1.0.0",
"ApplicationModelWebSite": "1.0.0",
"BasicWebSite": "1.0.0",
diff --git a/test/Microsoft.AspNet.Mvc.Razor.Test/RazorViewEngineTest.cs b/test/Microsoft.AspNet.Mvc.Razor.Test/RazorViewEngineTest.cs
index 83b1f488ef..34e4ff08df 100644
--- a/test/Microsoft.AspNet.Mvc.Razor.Test/RazorViewEngineTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Razor.Test/RazorViewEngineTest.cs
@@ -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()
{
diff --git a/test/WebSites/RoutingWebSite/Controllers/Products/ProductsController.cs b/test/WebSites/RoutingWebSite/Controllers/Products/ProductsController.cs
deleted file mode 100644
index 9333d2af28..0000000000
--- a/test/WebSites/RoutingWebSite/Controllers/Products/ProductsController.cs
+++ /dev/null
@@ -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");
- }
- }
-}
\ No newline at end of file
diff --git a/test/WebSites/RoutingWebSite/Controllers/Products/US/ProductsController.cs b/test/WebSites/RoutingWebSite/Controllers/Products/US/ProductsController.cs
deleted file mode 100644
index b47f8d593c..0000000000
--- a/test/WebSites/RoutingWebSite/Controllers/Products/US/ProductsController.cs
+++ /dev/null
@@ -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");
- }
- }
-}
\ No newline at end of file
diff --git a/test/WebSites/RoutingWebSite/CountryNeutralAttribute.cs b/test/WebSites/RoutingWebSite/CountryNeutralAttribute.cs
deleted file mode 100644
index 7622948673..0000000000
--- a/test/WebSites/RoutingWebSite/CountryNeutralAttribute.cs
+++ /dev/null
@@ -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)
- {
- }
- }
-}
\ No newline at end of file
diff --git a/test/WebSites/RoutingWebSite/CountrySpecificAttribute.cs b/test/WebSites/RoutingWebSite/CountrySpecificAttribute.cs
deleted file mode 100644
index c544abf4b7..0000000000
--- a/test/WebSites/RoutingWebSite/CountrySpecificAttribute.cs
+++ /dev/null
@@ -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)
- {
- }
- }
-}
\ No newline at end of file
diff --git a/test/WebSites/RoutingWebSite/Startup.cs b/test/WebSites/RoutingWebSite/Startup.cs
index ed86074c58..4acd9a15b2 100644
--- a/test/WebSites/RoutingWebSite/Startup.cs
+++ b/test/WebSites/RoutingWebSite/Startup.cs
@@ -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}",