diff --git a/samples/ActionConstraintSample.Web/CountrySpecificAttribute.cs b/samples/ActionConstraintSample.Web/CountrySpecificAttribute.cs index 4c19ad56c1..af192867ae 100644 --- a/samples/ActionConstraintSample.Web/CountrySpecificAttribute.cs +++ b/samples/ActionConstraintSample.Web/CountrySpecificAttribute.cs @@ -3,15 +3,14 @@ using System; using Microsoft.AspNetCore.Mvc.ActionConstraints; -using Microsoft.AspNetCore.Mvc.Routing; namespace ActionConstraintSample.Web { - public class CountrySpecificAttribute : RouteConstraintAttribute, IActionConstraint + public class CountrySpecificAttribute : Attribute, IActionConstraint { private readonly string _countryCode; + public CountrySpecificAttribute(string countryCode) - : base("country", countryCode, blockNonAttributedActions: false) { _countryCode = countryCode; } diff --git a/samples/CustomRouteSample.Web/LocaleAttribute.cs b/samples/CustomRouteSample.Web/LocaleAttribute.cs index 48f7df69e8..bf3cb0af0d 100644 --- a/samples/CustomRouteSample.Web/LocaleAttribute.cs +++ b/samples/CustomRouteSample.Web/LocaleAttribute.cs @@ -5,10 +5,10 @@ using Microsoft.AspNetCore.Mvc.Routing; namespace CustomRouteSample.Web { - public class LocaleAttribute : RouteConstraintAttribute + public class LocaleAttribute : RouteValueAttribute { public LocaleAttribute(string locale) - : base("locale", routeValue: locale, blockNonAttributedActions: true) + : base("locale", routeValue: locale) { } } diff --git a/samples/MvcSubAreaSample.Web/SubAreaAttribute.cs b/samples/MvcSubAreaSample.Web/SubAreaAttribute.cs index 8a1e62441b..d50244e9a9 100644 --- a/samples/MvcSubAreaSample.Web/SubAreaAttribute.cs +++ b/samples/MvcSubAreaSample.Web/SubAreaAttribute.cs @@ -7,10 +7,10 @@ using Microsoft.AspNetCore.Mvc.Routing; namespace MvcSubAreaSample.Web { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] - public class SubAreaAttribute : RouteConstraintAttribute + public class SubAreaAttribute : RouteValueAttribute { public SubAreaAttribute(string name) - : base("subarea", name, blockNonAttributedActions: true) + : base("subarea", name) { if (string.IsNullOrEmpty(name)) { diff --git a/samples/MvcSubAreaSample.Web/SubAreaViewLocationExpander.cs b/samples/MvcSubAreaSample.Web/SubAreaViewLocationExpander.cs index 1426b4ecdf..f0898b0dd8 100644 --- a/samples/MvcSubAreaSample.Web/SubAreaViewLocationExpander.cs +++ b/samples/MvcSubAreaSample.Web/SubAreaViewLocationExpander.cs @@ -29,12 +29,11 @@ namespace MvcSubAreaSample.Web public void PopulateValues(ViewLocationExpanderContext context) { - var subArea = context.ActionContext.ActionDescriptor.RouteConstraints.FirstOrDefault( - s => s.RouteKey == "subarea" && !string.IsNullOrEmpty(s.RouteValue)); - - if (subArea != null) + string subArea; + if (context.ActionContext.ActionDescriptor.RouteValues.TryGetValue(_subAreaKey, out subArea) && + !string.IsNullOrEmpty(subArea)) { - context.Values[_subAreaKey] = subArea.RouteValue; + context.Values[_subAreaKey] = subArea; } } } diff --git a/src/Microsoft.AspNetCore.Mvc.Abstractions/Abstractions/ActionDescriptor.cs b/src/Microsoft.AspNetCore.Mvc.Abstractions/Abstractions/ActionDescriptor.cs index 383ebf6154..be416aa30b 100644 --- a/src/Microsoft.AspNetCore.Mvc.Abstractions/Abstractions/ActionDescriptor.cs +++ b/src/Microsoft.AspNetCore.Mvc.Abstractions/Abstractions/ActionDescriptor.cs @@ -13,9 +13,10 @@ namespace Microsoft.AspNetCore.Mvc.Abstractions { public ActionDescriptor() { - Properties = new Dictionary(); - RouteValueDefaults = new Dictionary(StringComparer.OrdinalIgnoreCase); Id = Guid.NewGuid().ToString(); + Properties = new Dictionary(); + RouteValues = new Dictionary(StringComparer.OrdinalIgnoreCase); + RouteValueDefaults = new Dictionary(StringComparer.OrdinalIgnoreCase); } /// @@ -25,7 +26,11 @@ namespace Microsoft.AspNetCore.Mvc.Abstractions public virtual string Name { get; set; } - public IList RouteConstraints { get; set; } + /// + /// Gets or sets the collection of route values that must be provided by routing + /// for the action to be selected. + /// + public IDictionary RouteValues { get; set; } public AttributeRouteInfo AttributeRouteInfo { get; set; } diff --git a/src/Microsoft.AspNetCore.Mvc.Abstractions/Routing/RouteDataActionConstraint.cs b/src/Microsoft.AspNetCore.Mvc.Abstractions/Routing/RouteDataActionConstraint.cs deleted file mode 100644 index 870c7533ad..0000000000 --- a/src/Microsoft.AspNetCore.Mvc.Abstractions/Routing/RouteDataActionConstraint.cs +++ /dev/null @@ -1,63 +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 System; - -namespace Microsoft.AspNetCore.Mvc.Routing -{ - /// - /// Constraints an action to a route key and value. - /// - public class RouteDataActionConstraint - { - private RouteDataActionConstraint(string routeKey) - { - if (routeKey == null) - { - throw new ArgumentNullException(nameof(routeKey)); - } - - RouteKey = routeKey; - } - - /// - /// Initializes a with a key and value, that are - /// required to make the action match. - /// - /// The route key. - /// The route value. - /// - /// Passing a or to - /// is a way to express that routing cannot produce a value for this key. - /// - public RouteDataActionConstraint(string routeKey, string routeValue) - : this(routeKey) - { - RouteValue = routeValue ?? string.Empty; - - if (string.IsNullOrEmpty(routeValue)) - { - KeyHandling = RouteKeyHandling.DenyKey; - } - else - { - KeyHandling = RouteKeyHandling.RequireKey; - } - } - - /// - /// The route key this constraint matches against. - /// - public string RouteKey { get; private set; } - - /// - /// The route value this constraint matches against. - /// - public string RouteValue { get; private set; } - - /// - /// The key handling definition for this constraint. - /// - public RouteKeyHandling KeyHandling { get; private set; } - } -} diff --git a/src/Microsoft.AspNetCore.Mvc.Abstractions/Routing/RouteKeyHandling.cs b/src/Microsoft.AspNetCore.Mvc.Abstractions/Routing/RouteKeyHandling.cs deleted file mode 100644 index 586a09399f..0000000000 --- a/src/Microsoft.AspNetCore.Mvc.Abstractions/Routing/RouteKeyHandling.cs +++ /dev/null @@ -1,18 +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. - -namespace Microsoft.AspNetCore.Mvc.Routing -{ - public enum RouteKeyHandling - { - /// - /// Requires that the key will be in the route values, and that the content matches. - /// - RequireKey, - - /// - /// Requires that the key will not be in the route values. - /// - DenyKey, - } -} diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ApplicationModels/ActionModel.cs b/src/Microsoft.AspNetCore.Mvc.Core/ApplicationModels/ActionModel.cs index bec68b4949..fe72f7d786 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/ApplicationModels/ActionModel.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/ApplicationModels/ActionModel.cs @@ -6,8 +6,9 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; +using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.Filters; -using Microsoft.AspNetCore.Mvc.Routing; +using Microsoft.AspNetCore.Routing; namespace Microsoft.AspNetCore.Mvc.ApplicationModels { @@ -34,7 +35,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels Attributes = new List(attributes); Filters = new List(); Parameters = new List(); - RouteConstraints = new List(); + RouteValues = new Dictionary(StringComparer.OrdinalIgnoreCase); Properties = new Dictionary(); Selectors = new List(); } @@ -56,11 +57,11 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels Attributes = new List(other.Attributes); Filters = new List(other.Filters); Properties = new Dictionary(other.Properties); + RouteValues = new Dictionary(other.RouteValues, StringComparer.OrdinalIgnoreCase); // Make a deep copy of other 'model' types. ApiExplorer = new ApiExplorerModel(other.ApiExplorer); Parameters = new List(other.Parameters.Select(p => new ParameterModel(p))); - RouteConstraints = new List(other.RouteConstraints); Selectors = new List(other.Selectors.Select(s => new SelectorModel(s))); } @@ -88,7 +89,24 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels public IList Parameters { get; } - public IList RouteConstraints { get; } + /// + /// Gets a collection of route values that must be present in the + /// for the corresponding action to be selected. + /// + /// + /// + /// The value of is considered an implicit route value corresponding + /// to the key action and the value of is + /// considered an implicit route value corresponding to the key controller. These entries + /// will be added to , but will not be visible in + /// . + /// + /// + /// Entries in can override entries in + /// . + /// + /// + public IDictionary RouteValues { get; } /// /// Gets a set of properties associated with the action. diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ApplicationModels/ControllerModel.cs b/src/Microsoft.AspNetCore.Mvc.Core/ApplicationModels/ControllerModel.cs index 9c58a541ef..9b7df00cc2 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/ApplicationModels/ControllerModel.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/ApplicationModels/ControllerModel.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Reflection; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Routing; +using Microsoft.AspNetCore.Routing; namespace Microsoft.AspNetCore.Mvc.ApplicationModels { @@ -36,7 +37,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels ControllerProperties = new List(); Filters = new List(); Properties = new Dictionary(); - RouteConstraints = new List(); + RouteValues = new Dictionary(StringComparer.OrdinalIgnoreCase); Selectors = new List(); } @@ -56,7 +57,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels // These are just metadata, safe to create new collections Attributes = new List(other.Attributes); Filters = new List(other.Filters); - RouteConstraints = new List(other.RouteConstraints); + RouteValues = new Dictionary(other.RouteValues, StringComparer.OrdinalIgnoreCase); Properties = new Dictionary(other.Properties); // Make a deep copy of other 'model' types. @@ -97,7 +98,15 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels public IList Filters { get; } - public IList RouteConstraints { get; } + /// + /// Gets a collection of route values that must be present in the + /// for the corresponding action to be selected. + /// + /// + /// Entries in can be overridden by entries in + /// . + /// + public IDictionary RouteValues { get; } /// /// Gets a set of properties associated with the controller. diff --git a/src/Microsoft.AspNetCore.Mvc.Core/AreaAttribute.cs b/src/Microsoft.AspNetCore.Mvc.Core/AreaAttribute.cs index 3254128a99..09f860a740 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/AreaAttribute.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/AreaAttribute.cs @@ -7,10 +7,10 @@ using Microsoft.AspNetCore.Mvc.Routing; namespace Microsoft.AspNetCore.Mvc { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] - public class AreaAttribute : RouteConstraintAttribute + public class AreaAttribute : RouteValueAttribute { public AreaAttribute(string areaName) - : base("area", areaName, blockNonAttributedActions: true) + : base("area", areaName) { if (string.IsNullOrEmpty(areaName)) { diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Internal/ActionSelectionDecisionTree.cs b/src/Microsoft.AspNetCore.Mvc.Core/Internal/ActionSelectionDecisionTree.cs index b8db19146f..24d0e065b8 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/Internal/ActionSelectionDecisionTree.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/Internal/ActionSelectionDecisionTree.cs @@ -83,36 +83,13 @@ namespace Microsoft.AspNetCore.Mvc.Internal { var results = new Dictionary(StringComparer.OrdinalIgnoreCase); - if (item.RouteConstraints != null) + if (item.RouteValues != null) { - foreach (var constraint in item.RouteConstraints) + foreach (var kvp in item.RouteValues) { - DecisionCriterionValue value; - 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); - } - else if (constraint.KeyHandling == RouteKeyHandling.RequireKey) - { - value = new DecisionCriterionValue(value: constraint.RouteValue); - } - else - { - // We'd already have failed before getting here. The RouteDataActionConstraint constructor - // would throw. -#if NET451 - throw new InvalidEnumArgumentException( - nameof(item), - (int)constraint.KeyHandling, - typeof(RouteKeyHandling)); -#else - throw new ArgumentOutOfRangeException(nameof(item)); -#endif - } - - results.Add(constraint.RouteKey, value); + // null and string.Empty are equivalent for route values, so just treat nulls as + // string.Empty. + results.Add(kvp.Key, new DecisionCriterionValue(kvp.Value ?? string.Empty)); } } diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Internal/ActionSelector.cs b/src/Microsoft.AspNetCore.Mvc.Core/Internal/ActionSelector.cs index 5c8d4e84e3..1f54301bd5 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/Internal/ActionSelector.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/Internal/ActionSelector.cs @@ -48,14 +48,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal } var tree = _decisionTreeProvider.DecisionTree; - var matchingRouteConstraints = tree.Select(context.RouteData.Values); + var matchingRouteValues = tree.Select(context.RouteData.Values); var candidates = new List(); // Perf: Avoid allocations - for (var i = 0; i < matchingRouteConstraints.Count; i++) + for (var i = 0; i < matchingRouteValues.Count; i++) { - var action = matchingRouteConstraints[i]; + var action = matchingRouteValues[i]; var constraints = _actionConstraintCache.GetActionConstraints(context.HttpContext, action); candidates.Add(new ActionSelectorCandidate(action, constraints)); } diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Internal/AttributeRoute.cs b/src/Microsoft.AspNetCore.Mvc.Core/Internal/AttributeRoute.cs index fcec2c2e86..ba01c23259 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/Internal/AttributeRoute.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/Internal/AttributeRoute.cs @@ -179,11 +179,10 @@ namespace Microsoft.AspNetCore.Mvc.Internal Dictionary templateCache, ActionDescriptor action) { - var constraint = action.RouteConstraints - .FirstOrDefault(c => c.RouteKey == TreeRouter.RouteGroupKey); - if (constraint == null || - constraint.KeyHandling != RouteKeyHandling.RequireKey || - constraint.RouteValue == null) + string value; + action.RouteValues.TryGetValue(TreeRouter.RouteGroupKey, out value); + + if (string.IsNullOrEmpty(value)) { // This can happen if an ActionDescriptor has a route template, but doesn't have one of our // special route group constraints. This is a good indication that the user is using a 3rd party @@ -196,7 +195,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal var routeInfo = new RouteInfo() { ActionDescriptor = action, - RouteGroup = constraint.RouteValue, + RouteGroup = value, }; try diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Internal/ControllerActionDescriptorBuilder.cs b/src/Microsoft.AspNetCore.Mvc.Core/Internal/ControllerActionDescriptorBuilder.cs index 313bd1506e..9c504aa22c 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/Internal/ControllerActionDescriptorBuilder.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/Internal/ControllerActionDescriptorBuilder.cs @@ -37,7 +37,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal var actions = new List(); var hasAttributeRoutes = false; - var removalConstraints = new HashSet(StringComparer.OrdinalIgnoreCase); + var routeValueKeys = new HashSet(StringComparer.OrdinalIgnoreCase); var methodInfoMap = new MethodToActionMap(); @@ -67,7 +67,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal actionDescriptor.ControllerTypeInfo = controller.ControllerType; AddApiExplorerInfo(actionDescriptor, application, controller, action); - AddRouteConstraints(removalConstraints, actionDescriptor, controller, action); + AddRouteValues(routeValueKeys, actionDescriptor, controller, action); AddProperties(actionDescriptor, action, controller, application); actionDescriptor.BoundProperties = controllerPropertyDescriptors; @@ -77,15 +77,15 @@ namespace Microsoft.AspNetCore.Mvc.Internal // An attribute routed action will ignore conventional routed constraints. We still // want to provide these values as ambient values for link generation. - AddConstraintsAsDefaultRouteValues(actionDescriptor); + AddRouteValuesAsDefaultRouteValues(actionDescriptor); // Replaces tokens like [controller]/[action] in the route template with the actual values // for this action. ReplaceAttributeRouteTokens(actionDescriptor, routeTemplateErrors); - // Attribute routed actions will ignore conventional routed constraints. Instead they have - // a single route constraint "RouteGroup" associated with it. - ReplaceRouteConstraints(actionDescriptor); + // Attribute routed actions will ignore conventional routed values. Instead they have + // a single route value "RouteGroup" associated with it. + ReplaceRouteValues(actionDescriptor); } } @@ -119,16 +119,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal // selected when a route group returned by the route. if (hasAttributeRoutes) { - actionDescriptor.RouteConstraints.Add(new RouteDataActionConstraint( - TreeRouter.RouteGroupKey, - string.Empty)); + actionDescriptor.RouteValues.Add(TreeRouter.RouteGroupKey, string.Empty); } - // Add a route constraint with DenyKey for each constraint in the set to all the - // actions that don't have that constraint. For example, if a controller defines - // an area constraint, all actions that don't belong to an area must have a route - // constraint that prevents them from matching an incoming request. - AddRemovalConstraints(actionDescriptor, removalConstraints); + // Add a route value with 'null' for each user-defined route value in the set to all the + // actions that don't have that value. For example, if a controller defines + // an area, all actions that don't belong to an area must have a route + // value that prevents them from matching an incoming request when area is specified. + AddGlobalRouteValues(actionDescriptor, routeValueKeys); } else { @@ -145,7 +143,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal // // Consider an action like { area = "", controller = "Home", action = "Index" }. Even if // it's attribute routed, it needs to know that area must be null to generate a link. - foreach (var key in removalConstraints) + foreach (var key in routeValueKeys) { if (!actionDescriptor.RouteValueDefaults.ContainsKey(key)) { @@ -287,7 +285,6 @@ namespace Microsoft.AspNetCore.Mvc.Internal Name = action.ActionName, MethodInfo = action.ActionMethod, Parameters = parameterDescriptors, - RouteConstraints = new List(), AttributeRouteInfo = CreateAttributeRouteInfo(actionAttributeRoute, controllerAttributeRoute) }; @@ -448,8 +445,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal } } - public static void AddRouteConstraints( - ISet removalConstraints, + public static void AddRouteValues( + ISet keys, ControllerActionDescriptor actionDescriptor, ControllerModel controller, ActionModel action) @@ -459,72 +456,48 @@ namespace Microsoft.AspNetCore.Mvc.Internal // without the constraint to match. For example, actions without an [Area] attribute on their // controller should not match when a value has been given for area when matching a url or // generating a link. - foreach (var constraintAttribute in action.RouteConstraints) + foreach (var kvp in action.RouteValues) { - if (constraintAttribute.BlockNonAttributedActions) - { - removalConstraints.Add(constraintAttribute.RouteKey); - } - + keys.Add(kvp.Key); + // Skip duplicates - if (!HasConstraint(actionDescriptor.RouteConstraints, constraintAttribute.RouteKey)) + if (!actionDescriptor.RouteValues.ContainsKey(kvp.Key)) { - actionDescriptor.RouteConstraints.Add(new RouteDataActionConstraint( - constraintAttribute.RouteKey, - constraintAttribute.RouteValue)); + actionDescriptor.RouteValues.Add(kvp.Key, kvp.Value); } } - foreach (var constraintAttribute in controller.RouteConstraints) + foreach (var kvp in controller.RouteValues) { - if (constraintAttribute.BlockNonAttributedActions) - { - removalConstraints.Add(constraintAttribute.RouteKey); - } + keys.Add(kvp.Key); // Skip duplicates - this also means that a value on the action will take precedence - if (!HasConstraint(actionDescriptor.RouteConstraints, constraintAttribute.RouteKey)) + if (!actionDescriptor.RouteValues.ContainsKey(kvp.Key)) { - actionDescriptor.RouteConstraints.Add(new RouteDataActionConstraint( - constraintAttribute.RouteKey, - constraintAttribute.RouteValue)); + actionDescriptor.RouteValues.Add(kvp.Key, kvp.Value); } } // Lastly add the 'default' values - if (!HasConstraint(actionDescriptor.RouteConstraints, "action")) + if (!actionDescriptor.RouteValues.ContainsKey("action")) { - actionDescriptor.RouteConstraints.Add(new RouteDataActionConstraint( - "action", - action.ActionName ?? string.Empty)); + actionDescriptor.RouteValues.Add("action", action.ActionName ?? string.Empty); } - if (!HasConstraint(actionDescriptor.RouteConstraints, "controller")) + if (!actionDescriptor.RouteValues.ContainsKey("controller")) { - actionDescriptor.RouteConstraints.Add(new RouteDataActionConstraint( - "controller", - controller.ControllerName)); + actionDescriptor.RouteValues.Add("controller", controller.ControllerName); } } - private static bool HasConstraint(IList constraints, string routeKey) - { - return constraints.Any( - rc => string.Equals(rc.RouteKey, routeKey, StringComparison.OrdinalIgnoreCase)); - } - - private static void ReplaceRouteConstraints(ControllerActionDescriptor actionDescriptor) + private static void ReplaceRouteValues(ControllerActionDescriptor actionDescriptor) { var routeGroupValue = GetRouteGroupValue( actionDescriptor.AttributeRouteInfo.Order, actionDescriptor.AttributeRouteInfo.Template); - var routeConstraints = new List(); - routeConstraints.Add(new RouteDataActionConstraint( - TreeRouter.RouteGroupKey, - routeGroupValue)); - - actionDescriptor.RouteConstraints = routeConstraints; + actionDescriptor.RouteValues.Clear(); + actionDescriptor.RouteValues.Add(TreeRouter.RouteGroupKey, routeGroupValue); } private static void ReplaceAttributeRouteTokens( @@ -557,31 +530,23 @@ namespace Microsoft.AspNetCore.Mvc.Internal } } - private static void AddConstraintsAsDefaultRouteValues(ControllerActionDescriptor actionDescriptor) + private static void AddRouteValuesAsDefaultRouteValues(ControllerActionDescriptor actionDescriptor) { - foreach (var constraint in actionDescriptor.RouteConstraints) + foreach (var kvp in actionDescriptor.RouteValues) { - // We don't need to do anything with attribute routing for 'catch all' behavior. Order - // and precedence of attribute routes allow this kind of behavior. - if (constraint.KeyHandling == RouteKeyHandling.RequireKey || - constraint.KeyHandling == RouteKeyHandling.DenyKey) - { - actionDescriptor.RouteValueDefaults.Add(constraint.RouteKey, constraint.RouteValue); - } + actionDescriptor.RouteValueDefaults.Add(kvp.Key, kvp.Value); } } - private static void AddRemovalConstraints( + private static void AddGlobalRouteValues( ControllerActionDescriptor actionDescriptor, ISet removalConstraints) { foreach (var key in removalConstraints) { - if (!HasConstraint(actionDescriptor.RouteConstraints, key)) + if (!actionDescriptor.RouteValues.ContainsKey(key)) { - actionDescriptor.RouteConstraints.Add(new RouteDataActionConstraint( - key, - string.Empty)); + actionDescriptor.RouteValues.Add(key, string.Empty); } } } diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Internal/DefaultApplicationModelProvider.cs b/src/Microsoft.AspNetCore.Mvc.Core/Internal/DefaultApplicationModelProvider.cs index 708a355011..9b9c345df0 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/Internal/DefaultApplicationModelProvider.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/Internal/DefaultApplicationModelProvider.cs @@ -170,7 +170,11 @@ namespace Microsoft.AspNetCore.Mvc.Internal typeInfo.Name; AddRange(controllerModel.Filters, attributes.OfType()); - AddRange(controllerModel.RouteConstraints, attributes.OfType()); + + foreach (var routeValueProvider in attributes.OfType()) + { + controllerModel.RouteValues.Add(routeValueProvider.RouteKey, routeValueProvider.RouteValue); + } var apiVisibility = attributes.OfType().FirstOrDefault(); if (apiVisibility != null) @@ -285,8 +289,11 @@ namespace Microsoft.AspNetCore.Mvc.Internal actionModel.ApiExplorer.GroupName = apiGroupName.GroupName; } - AddRange(actionModel.RouteConstraints, attributes.OfType()); - + foreach (var routeValueProvider in attributes.OfType()) + { + actionModel.RouteValues.Add(routeValueProvider.RouteKey, routeValueProvider.RouteValue); + } + //TODO: modify comment // Now we need to determine the action selection info (cross-section of routes and constraints) // diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Internal/RouteConstraintAttribute.cs b/src/Microsoft.AspNetCore.Mvc.Core/Internal/RouteConstraintAttribute.cs deleted file mode 100644 index 1e91951e01..0000000000 --- a/src/Microsoft.AspNetCore.Mvc.Core/Internal/RouteConstraintAttribute.cs +++ /dev/null @@ -1,78 +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 System; - -namespace Microsoft.AspNetCore.Mvc.Routing -{ - /// - /// An attribute which specifies a required route value for an action or controller. - /// - /// When placed on an action, the route data of a request must match the expectations of the route - /// constraint in order for the action to be selected. See for - /// the expectations that must be satisfied by the route data. - /// - /// When placed on a controller, unless overridden by the action, the constraint applies to all - /// actions defined by the controller. - /// - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)] - public abstract class RouteConstraintAttribute : Attribute, IRouteConstraintProvider - { - /// - /// Creates a new with set as - /// . - /// - /// The route value key. - protected RouteConstraintAttribute(string routeKey) - { - if (routeKey == null) - { - throw new ArgumentNullException(nameof(routeKey)); - } - - RouteKey = routeKey; - RouteKeyHandling = RouteKeyHandling.DenyKey; - } - - /// - /// Creates a new with - /// set to . - /// - /// The route value key. - /// The expected route value. - /// - /// Set to true to negate this constraint on all actions that do not define a behavior for this route key. - /// - protected RouteConstraintAttribute( - string routeKey, - string routeValue, - bool blockNonAttributedActions) - { - if (routeKey == null) - { - throw new ArgumentNullException(nameof(routeKey)); - } - - if (routeValue == null) - { - throw new ArgumentNullException(nameof(routeValue)); - } - - RouteKey = routeKey; - RouteValue = routeValue; - BlockNonAttributedActions = blockNonAttributedActions; - } - - /// - public string RouteKey { get; private set; } - - /// - public RouteKeyHandling RouteKeyHandling { get; private set; } - - /// - public string RouteValue { get; private set; } - - /// - public bool BlockNonAttributedActions { get; private set; } - } -} diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Routing/IRouteConstraintProvider.cs b/src/Microsoft.AspNetCore.Mvc.Core/Routing/IRouteConstraintProvider.cs deleted file mode 100644 index 4d7fb4a800..0000000000 --- a/src/Microsoft.AspNetCore.Mvc.Core/Routing/IRouteConstraintProvider.cs +++ /dev/null @@ -1,33 +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. - -namespace Microsoft.AspNetCore.Mvc.Routing -{ - /// - /// An interface for metadata which provides values - /// for a controller or action. - /// - public interface IRouteConstraintProvider - { - /// - /// The route value key. - /// - string RouteKey { get; } - - /// - /// The . - /// - RouteKeyHandling RouteKeyHandling { get; } - - /// - /// The expected route value. Will be null unless is - /// set to . - /// - string RouteValue { get; } - - /// - /// Set to true to negate this constraint on all actions that do not define a behavior for this route key. - /// - bool BlockNonAttributedActions { get; } - } -} diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Routing/IRouteValueProvider.cs b/src/Microsoft.AspNetCore.Mvc.Core/Routing/IRouteValueProvider.cs new file mode 100644 index 0000000000..9917c825f2 --- /dev/null +++ b/src/Microsoft.AspNetCore.Mvc.Core/Routing/IRouteValueProvider.cs @@ -0,0 +1,85 @@ +// 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; +using Microsoft.AspNetCore.Mvc.Abstractions; +using Microsoft.AspNetCore.Mvc.Controllers; + +namespace Microsoft.AspNetCore.Mvc.Routing +{ + /// + /// + /// A metadata interface which specifies a route value which is required for the action selector to + /// choose an action. When applied to an action using attribute routing, the route value will be added + /// to the when the action is selected. + /// + /// + /// When an is used to provide a new route value to an action, all + /// actions in the application must also have a value associated with that key, or have an implicit value + /// of null. See remarks for more details. + /// + /// + /// + /// + /// The typical scheme for action selection in an MVC application is that an action will require the + /// matching values for its and + /// + /// + /// + /// For an action like MyApp.Controllers.HomeController.Index(), in order to be selected, the + /// must contain the values + /// { + /// "action": "Index", + /// "controller": "Home" + /// } + /// + /// + /// If areas are in use in the application (see which implements + /// ) then all actions are consider either in an area by having a + /// non-null area value (specified by or another + /// ) or are considered 'outside' of areas by having the value null. + /// + /// + /// Consider an application with two controllers, each with an Index action method: + /// - MyApp.Controllers.HomeController.Index() + /// - MyApp.Areas.Blog.Controllers.HomeController.Index() + /// where MyApp.Areas.Blog.Controllers.HomeController has an area attribute + /// [Area("Blog")]. + /// + /// For like: + /// { + /// "action": "Index", + /// "controller": "Home" + /// } + /// + /// MyApp.Controllers.HomeController.Index() will be selected. + /// MyApp.Area.Blog.Controllers.HomeController.Index() is not considered eligible because the + /// does not contain the value 'Blog' for 'area'. + /// + /// For like: + /// { + /// "area": "Blog", + /// "action": "Index", + /// "controller": "Home" + /// } + /// + /// MyApp.Area.Blog.Controllers.HomeController.Index() will be selected. + /// MyApp.Controllers.HomeController.Index() is not considered eligible because the route values + /// contain a value for 'area'. MyApp.Controllers.HomeController.Index() cannot match any value + /// for 'area' other than null. + /// + /// + public interface IRouteValueProvider + { + /// + /// The route value key. + /// + string RouteKey { get; } + + /// + /// The route value. If null or empty, requires the route value associated with + /// to be missing or null. + /// + string RouteValue { get; } + } +} diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Routing/KnownRouteValueConstraint.cs b/src/Microsoft.AspNetCore.Mvc.Core/Routing/KnownRouteValueConstraint.cs index bb629f52d6..30a57e87cd 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/Routing/KnownRouteValueConstraint.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/Routing/KnownRouteValueConstraint.cs @@ -2,6 +2,7 @@ // 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 Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Core; @@ -71,18 +72,20 @@ namespace Microsoft.AspNetCore.Mvc.Routing if (valuesCollection == null || version != valuesCollection.Version) { - var routeValueCollection = - actionDescriptors - .Items - .Select(ad => ad.RouteConstraints.FirstOrDefault( - c => c.RouteKey == routeKey && - c.KeyHandling == RouteKeyHandling.RequireKey)) - .Where(rc => rc != null) - .Select(rc => rc.RouteValue) - .Distinct() - .ToArray(); + var values = new HashSet(StringComparer.OrdinalIgnoreCase); + for (var i = 0; i < actionDescriptors.Items.Count; i++) + { + var action = actionDescriptors.Items[i]; - valuesCollection = new RouteValuesCollection(version, routeValueCollection); + string value; + if (action.RouteValues.TryGetValue(routeKey, out value) && + !string.IsNullOrEmpty(value)) + { + values.Add(value); + } + } + + valuesCollection = new RouteValuesCollection(version, values.ToArray()); _cachedValuesCollection = valuesCollection; } diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Routing/RouteValueAttribute.cs b/src/Microsoft.AspNetCore.Mvc.Core/Routing/RouteValueAttribute.cs new file mode 100644 index 0000000000..5131b3fbf4 --- /dev/null +++ b/src/Microsoft.AspNetCore.Mvc.Core/Routing/RouteValueAttribute.cs @@ -0,0 +1,50 @@ +// 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; + +namespace Microsoft.AspNetCore.Mvc.Routing +{ + /// + /// An attribute which specifies a required route value for an action or controller. + /// + /// When placed on an action, the route data of a request must match the expectations of the route + /// constraint in order for the action to be selected. See for + /// the expectations that must be satisfied by the route data. + /// + /// When placed on a controller, unless overridden by the action, the constraint applies to all + /// actions defined by the controller. + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)] + public abstract class RouteValueAttribute : Attribute, IRouteValueProvider + { + /// + /// Creates a new . + /// + /// The route value key. + /// The expected route value. + protected RouteValueAttribute( + string routeKey, + string routeValue) + { + if (routeKey == null) + { + throw new ArgumentNullException(nameof(routeKey)); + } + + if (routeValue == null) + { + throw new ArgumentNullException(nameof(routeValue)); + } + + RouteKey = routeKey; + RouteValue = routeValue; + } + + /// + public string RouteKey { get; } + + /// + public string RouteValue { get; } + } +} diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/RazorViewEngine.cs b/src/Microsoft.AspNetCore.Mvc.Razor/RazorViewEngine.cs index 6c422b13ae..09a333451d 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor/RazorViewEngine.cs +++ b/src/Microsoft.AspNetCore.Mvc.Razor/RazorViewEngine.cs @@ -116,7 +116,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor /// The casing of a route value in is determined by the client. /// This making constructing paths for view locations in a case sensitive file system unreliable. Using the /// for attribute routes and - /// for traditional routes to get route values + /// for traditional routes to get route values /// produces consistently cased results. /// public static string GetNormalizedRouteValue(ActionContext context, string key) @@ -149,24 +149,11 @@ namespace Microsoft.AspNetCore.Mvc.Razor } else { - // Perf: Avoid allocations - for (var i = 0; i < actionDescriptor.RouteConstraints.Count; i++) + string value; + if (actionDescriptor.RouteValues.TryGetValue(key, out value) && + !string.IsNullOrEmpty(value)) { - var constraint = actionDescriptor.RouteConstraints[i]; - if (string.Equals(constraint.RouteKey, key, StringComparison.Ordinal)) - { - if (constraint.KeyHandling == RouteKeyHandling.DenyKey) - { - return null; - } - else - { - normalizedValue = constraint.RouteValue; - } - - // Duplicate keys in RouteConstraints are not allowed. - break; - } + normalizedValue = value; } } diff --git a/src/Microsoft.AspNetCore.Mvc.WebApiCompatShim/Conventions/WebApiActionConventionsApplicationModelConvention.cs b/src/Microsoft.AspNetCore.Mvc.WebApiCompatShim/Conventions/WebApiActionConventionsApplicationModelConvention.cs index e5acb57f82..473277cdec 100644 --- a/src/Microsoft.AspNetCore.Mvc.WebApiCompatShim/Conventions/WebApiActionConventionsApplicationModelConvention.cs +++ b/src/Microsoft.AspNetCore.Mvc.WebApiCompatShim/Conventions/WebApiActionConventionsApplicationModelConvention.cs @@ -45,7 +45,7 @@ namespace Microsoft.AspNetCore.Mvc.WebApiCompatShim var namedAction = action; var unnamedAction = new ActionModel(namedAction); - unnamedAction.RouteConstraints.Add(new UnnamedActionRouteConstraint()); + unnamedAction.RouteValues.Add("action", null); newActions.Add(unnamedAction); } } @@ -114,23 +114,5 @@ namespace Microsoft.AspNetCore.Mvc.WebApiCompatShim actionSelectorModel.ActionConstraints.Add(new HttpMethodActionConstraint(new[] { "POST" })); } } - - private class UnnamedActionRouteConstraint : IRouteConstraintProvider - { - public UnnamedActionRouteConstraint() - { - RouteKey = "action"; - RouteKeyHandling = RouteKeyHandling.DenyKey; - RouteValue = null; - } - - public string RouteKey { get; } - - public RouteKeyHandling RouteKeyHandling { get; } - - public string RouteValue { get; } - - public bool BlockNonAttributedActions { get; } - } } } diff --git a/src/Microsoft.AspNetCore.Mvc.WebApiCompatShim/Conventions/WebApiRoutesApplicationModelConvention.cs b/src/Microsoft.AspNetCore.Mvc.WebApiCompatShim/Conventions/WebApiRoutesApplicationModelConvention.cs index 6c9018312f..cb397dcd74 100644 --- a/src/Microsoft.AspNetCore.Mvc.WebApiCompatShim/Conventions/WebApiRoutesApplicationModelConvention.cs +++ b/src/Microsoft.AspNetCore.Mvc.WebApiCompatShim/Conventions/WebApiRoutesApplicationModelConvention.cs @@ -25,7 +25,7 @@ namespace Microsoft.AspNetCore.Mvc.WebApiCompatShim if (IsConventionApplicable(controller)) { - controller.RouteConstraints.Add(new AreaAttribute(_area)); + controller.RouteValues.Add("area", _area); } } diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/ApplicationModel/ActionModelTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/ApplicationModel/ActionModelTest.cs index f29cda2d0c..e72a567a9c 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/ApplicationModel/ActionModelTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/ApplicationModel/ActionModelTest.cs @@ -64,10 +64,11 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels action.Selectors.Add(selectorModel); action.ActionName = "Edit"; - action.Controller = new ControllerModel(typeof(TestController).GetTypeInfo(), - new List()); + action.Controller = new ControllerModel + (typeof(TestController).GetTypeInfo(), + new List()); action.Filters.Add(new MyFilterAttribute()); - action.RouteConstraints.Add(new MyRouteConstraintAttribute()); + action.RouteValues.Add("key", "value"); action.Properties.Add(new KeyValuePair("test key", "test value")); // Act @@ -95,6 +96,13 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels // Ensure non-default value Assert.NotEmpty((IEnumerable)value1); } + else if (typeof(IDictionary).IsAssignableFrom(property.PropertyType)) + { + Assert.Equal(value1, value2); + + // Ensure non-default value + Assert.NotEmpty((IDictionary)value1); + } else if (typeof(IDictionary).IsAssignableFrom(property.PropertyType)) { Assert.Equal(value1, value2); @@ -131,14 +139,10 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels { } - private class MyRouteConstraintAttribute : Attribute, IRouteConstraintProvider + private class MyRouteValueAttribute : Attribute, IRouteValueProvider { - public bool BlockNonAttributedActions { get { return true; } } - public string RouteKey { get; set; } - public RouteKeyHandling RouteKeyHandling { get { return RouteKeyHandling.RequireKey; } } - public string RouteValue { get; set; } } } diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/ApplicationModel/ControllerModelTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/ApplicationModel/ControllerModelTest.cs index 497004adfe..8e99a139c3 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/ApplicationModel/ControllerModelTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/ApplicationModel/ControllerModelTest.cs @@ -48,7 +48,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels Assert.NotSame(controller.Actions, controller2.Actions); Assert.NotSame(controller.Attributes, controller2.Attributes); Assert.NotSame(controller.Filters, controller2.Filters); - Assert.NotSame(controller.RouteConstraints, controller2.RouteConstraints); + Assert.NotSame(controller.RouteValues, controller2.RouteValues); } [Fact] @@ -69,7 +69,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels controller.Application = new ApplicationModel(); controller.ControllerName = "cool"; controller.Filters.Add(new MyFilterAttribute()); - controller.RouteConstraints.Add(new MyRouteConstraintAttribute()); + controller.RouteValues.Add("key", "value"); controller.Properties.Add(new KeyValuePair("test key", "test value")); controller.ControllerProperties.Add( new PropertyModel(typeof(TestController).GetProperty("TestProperty"), new List())); @@ -99,6 +99,13 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels // Ensure non-default value Assert.NotEmpty((IEnumerable)value1); } + else if (typeof(IDictionary).IsAssignableFrom(property.PropertyType)) + { + Assert.Equal(value1, value2); + + // Ensure non-default value + Assert.NotEmpty((IDictionary)value1); + } else if (typeof(IDictionary).IsAssignableFrom(property.PropertyType)) { Assert.Equal(value1, value2); @@ -137,14 +144,10 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels { } - private class MyRouteConstraintAttribute : Attribute, IRouteConstraintProvider + private class MyRouteValueAttribute : Attribute, IRouteValueProvider { - public bool BlockNonAttributedActions { get { return true; } } - public string RouteKey { get; set; } - public RouteKeyHandling RouteKeyHandling { get { return RouteKeyHandling.RequireKey; } } - public string RouteValue { get; set; } } } diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Infrastructure/DefaultActionSelectorTests.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Infrastructure/DefaultActionSelectorTests.cs index 366ec69656..d540f14f6f 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Infrastructure/DefaultActionSelectorTests.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Infrastructure/DefaultActionSelectorTests.cs @@ -600,9 +600,9 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure return actions - .Where(a => a.RouteConstraints.Any(c => c.RouteKey == "area" && comparer.Equals(c.RouteValue, area))) - .Where(a => a.RouteConstraints.Any(c => c.RouteKey == "controller" && comparer.Equals(c.RouteValue, controller))) - .Where(a => a.RouteConstraints.Any(c => c.RouteKey == "action" && comparer.Equals(c.RouteValue, action))); + .Where(a => a.RouteValues.Any(kvp => kvp.Key == "area" && comparer.Equals(kvp.Value, area))) + .Where(a => a.RouteValues.Any(kvp => kvp.Key == "controller" && comparer.Equals(kvp.Value, controller))) + .Where(a => a.RouteValues.Any(kvp => kvp.Key == "action" && comparer.Equals(kvp.Value, action))); } private static ActionSelector CreateSelector(IReadOnlyList actions, ILoggerFactory loggerFactory = null) @@ -667,24 +667,12 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure var actionDescriptor = new ActionDescriptor() { Name = string.Format("Area: {0}, Controller: {1}, Action: {2}", area, controller, action), - RouteConstraints = new List(), Parameters = new List(), }; - actionDescriptor.RouteConstraints.Add( - area == null ? - new RouteDataActionConstraint("area", null) : - new RouteDataActionConstraint("area", area)); - - actionDescriptor.RouteConstraints.Add( - controller == null ? - new RouteDataActionConstraint("controller", null) : - new RouteDataActionConstraint("controller", controller)); - - actionDescriptor.RouteConstraints.Add( - action == null ? - new RouteDataActionConstraint("action", null) : - new RouteDataActionConstraint("action", action)); + actionDescriptor.RouteValues.Add("area", area); + actionDescriptor.RouteValues.Add("controller", controller); + actionDescriptor.RouteValues.Add("action", action); return actionDescriptor; } diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Infrastructure/RouteDataActionConstraintTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Infrastructure/RouteDataActionConstraintTest.cs deleted file mode 100644 index 528289ad9e..0000000000 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Infrastructure/RouteDataActionConstraintTest.cs +++ /dev/null @@ -1,41 +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.AspNetCore.Mvc.Routing; -using Xunit; - -namespace Microsoft.AspNetCore.Mvc.Infrastructure -{ - public class RouteDataActionConstraintTest - { - [Fact] - public void RouteDataActionConstraint_DenyKeyByPassingEmptyString() - { - var routeDataConstraint = new RouteDataActionConstraint("key", string.Empty); - - Assert.Equal(routeDataConstraint.RouteKey, "key"); - Assert.Equal(routeDataConstraint.KeyHandling, RouteKeyHandling.DenyKey); - Assert.Equal(routeDataConstraint.RouteValue, string.Empty); - } - - [Fact] - public void RouteDataActionConstraint_DenyKeyByPassingNull() - { - var routeDataConstraint = new RouteDataActionConstraint("key", null); - - Assert.Equal(routeDataConstraint.RouteKey, "key"); - Assert.Equal(routeDataConstraint.KeyHandling, RouteKeyHandling.DenyKey); - Assert.Equal(routeDataConstraint.RouteValue, string.Empty); - } - - [Fact] - public void RouteDataActionConstraint_RequireKeyByPassingNonEmpty() - { - var routeDataConstraint = new RouteDataActionConstraint("key", "value"); - - Assert.Equal(routeDataConstraint.RouteKey, "key"); - Assert.Equal(routeDataConstraint.KeyHandling, RouteKeyHandling.RequireKey); - Assert.Equal(routeDataConstraint.RouteValue, "value"); - } - } -} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/AttributeRouteTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/AttributeRouteTest.cs index 305c737e57..6b21dea251 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/AttributeRouteTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/AttributeRouteTest.cs @@ -42,9 +42,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal { Template = "api/Blog/{id}" }, - RouteConstraints = new List() + RouteValues = new Dictionary(StringComparer.OrdinalIgnoreCase) { - new RouteDataActionConstraint(TreeRouter.RouteGroupKey, "1"), + { TreeRouter.RouteGroupKey, "1" } }, }, new ActionDescriptor() @@ -53,9 +53,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal { Template = "api/Store/Buy/{id}" }, - RouteConstraints = new List() + RouteValues = new Dictionary(StringComparer.OrdinalIgnoreCase) { - new RouteDataActionConstraint(TreeRouter.RouteGroupKey, "2"), + { TreeRouter.RouteGroupKey, "2" } }, }, }; @@ -116,9 +116,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal Name = "BLOG_INDEX", Order = 17, }, - RouteConstraints = new List() + RouteValues = new Dictionary(StringComparer.OrdinalIgnoreCase) { - new RouteDataActionConstraint(TreeRouter.RouteGroupKey, "1"), + { TreeRouter.RouteGroupKey, "1" } }, RouteValueDefaults = new Dictionary() { @@ -164,9 +164,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal Name = "BLOG_INDEX", Order = 17, }, - RouteConstraints = new List() + RouteValues = new Dictionary(StringComparer.OrdinalIgnoreCase) { - new RouteDataActionConstraint(TreeRouter.RouteGroupKey, "1"), + { TreeRouter.RouteGroupKey, "1" } }, RouteValueDefaults = new Dictionary() { @@ -212,9 +212,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal Name = "BLOG_INDEX", Order = 17, }, - RouteConstraints = new List() + RouteValues = new Dictionary(StringComparer.OrdinalIgnoreCase) { - new RouteDataActionConstraint(TreeRouter.RouteGroupKey, "1"), + { TreeRouter.RouteGroupKey, "1" } }, RouteValueDefaults = new Dictionary() { @@ -263,9 +263,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal Name = "BLOG_INDEX", Order = 17, }, - RouteConstraints = new List() + RouteValues = new Dictionary(StringComparer.OrdinalIgnoreCase) { - new RouteDataActionConstraint(TreeRouter.RouteGroupKey, "1"), + { TreeRouter.RouteGroupKey, "1" } }, RouteValueDefaults = new Dictionary() { @@ -281,9 +281,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal Name = "BLOG_INDEX", Order = 17, }, - RouteConstraints = new List() + RouteValues = new Dictionary(StringComparer.OrdinalIgnoreCase) { - new RouteDataActionConstraint(TreeRouter.RouteGroupKey, "1"), + { TreeRouter.RouteGroupKey, "1" } }, RouteValueDefaults = new Dictionary() { @@ -339,9 +339,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal Name = "BLOG_INDEX", Order = 17, }, - RouteConstraints = new List() + RouteValues = new Dictionary(StringComparer.OrdinalIgnoreCase) { - new RouteDataActionConstraint(TreeRouter.RouteGroupKey, "1"), + { TreeRouter.RouteGroupKey, "1" } }, RouteValueDefaults = new Dictionary() { @@ -388,9 +388,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal Name = "BLOG_INDEX", Order = 17, }, - RouteConstraints = new List() + RouteValues = new Dictionary(StringComparer.OrdinalIgnoreCase) { - new RouteDataActionConstraint(TreeRouter.RouteGroupKey, "1"), + { TreeRouter.RouteGroupKey, "1" } }, RouteValueDefaults = new Dictionary() { @@ -437,9 +437,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal Name = "BLOG_INDEX", Order = 17, }, - RouteConstraints = new List() + RouteValues = new Dictionary(StringComparer.OrdinalIgnoreCase) { - new RouteDataActionConstraint(TreeRouter.RouteGroupKey, "1"), + { TreeRouter.RouteGroupKey, "1" } }, RouteValueDefaults = new Dictionary() { @@ -490,9 +490,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal Name = "BLOG_INDEX", Order = 17, }, - RouteConstraints = new List() + RouteValues = new Dictionary(StringComparer.OrdinalIgnoreCase) { - new RouteDataActionConstraint(TreeRouter.RouteGroupKey, "1"), + { TreeRouter.RouteGroupKey, "1" } }, RouteValueDefaults = new Dictionary() { @@ -508,9 +508,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal Name = "BLOG_INDEX", Order = 17, }, - RouteConstraints = new List() + RouteValues = new Dictionary(StringComparer.OrdinalIgnoreCase) { - new RouteDataActionConstraint(TreeRouter.RouteGroupKey, "1"), + { TreeRouter.RouteGroupKey, "1" } }, RouteValueDefaults = new Dictionary() { diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/AttributeRoutingTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/AttributeRoutingTest.cs index 9143611aac..e597961ef1 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/AttributeRoutingTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/AttributeRoutingTest.cs @@ -131,9 +131,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal var action = new ControllerActionDescriptor(); action.DisplayName = "Microsoft.AspNetCore.Mvc.Routing.AttributeRoutingTest+HomeController.Index"; action.MethodInfo = actionMethod; - action.RouteConstraints = new List() + action.RouteValues = new Dictionary(StringComparer.OrdinalIgnoreCase) { - new RouteDataActionConstraint(TreeRouter.RouteGroupKey, "group"), + { TreeRouter.RouteGroupKey, "group" } }; action.AttributeRouteInfo = new AttributeRouteInfo(); action.AttributeRouteInfo.Template = "{controller}/{action}"; @@ -167,9 +167,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal return new DisplayNameActionDescriptor() { DisplayName = displayName, - RouteConstraints = new List() + RouteValues = new Dictionary(StringComparer.OrdinalIgnoreCase) { - new RouteDataActionConstraint(TreeRouter.RouteGroupKey, "whatever"), + { TreeRouter.RouteGroupKey, "whatever" } }, AttributeRouteInfo = new AttributeRouteInfo { Template = template }, }; diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ControllerActionDescriptorProviderTests.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ControllerActionDescriptorProviderTests.cs index 63e05de598..e49e1cb5e4 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ControllerActionDescriptorProviderTests.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ControllerActionDescriptorProviderTests.cs @@ -242,17 +242,13 @@ namespace Microsoft.AspNetCore.Mvc.Internal // Assert var action = Assert.Single(descriptors); - Assert.NotNull(action.RouteConstraints); + Assert.NotNull(action.RouteValues); - var controller = Assert.Single(action.RouteConstraints, - rc => rc.RouteKey.Equals("controller")); - Assert.Equal(RouteKeyHandling.RequireKey, controller.KeyHandling); - Assert.Equal("ConventionallyRouted", controller.RouteValue); + var controller = Assert.Single(action.RouteValues, kvp => kvp.Key.Equals("controller")); + Assert.Equal("ConventionallyRouted", controller.Value); - var actionConstraint = Assert.Single(action.RouteConstraints, - rc => rc.RouteKey.Equals("action")); - Assert.Equal(RouteKeyHandling.RequireKey, actionConstraint.KeyHandling); - Assert.Equal(nameof(ConventionallyRoutedController.ConventionalAction), actionConstraint.RouteValue); + var actionConstraint = Assert.Single(action.RouteValues, kvp => kvp.Key.Equals("action")); + Assert.Equal(nameof(ConventionallyRoutedController.ConventionalAction), actionConstraint.Value); } [Fact] @@ -265,132 +261,67 @@ namespace Microsoft.AspNetCore.Mvc.Internal // Assert var action = Assert.Single(descriptors); - var routeconstraint = Assert.Single(action.RouteConstraints); - Assert.Equal(RouteKeyHandling.RequireKey, routeconstraint.KeyHandling); - Assert.Equal(TreeRouter.RouteGroupKey, routeconstraint.RouteKey); + Assert.Equal(TreeRouter.RouteGroupKey, Assert.Single(action.RouteValues).Key); - var controller = Assert.Single(action.RouteValueDefaults, - rc => rc.Key.Equals("controller")); + var controller = Assert.Single(action.RouteValueDefaults, kvp => kvp.Key.Equals("controller")); Assert.Equal("AttributeRouted", controller.Value); - var actionConstraint = Assert.Single(action.RouteValueDefaults, - rc => rc.Key.Equals("action")); + var actionConstraint = Assert.Single(action.RouteValueDefaults, kvp => kvp.Key.Equals("action")); Assert.Equal(nameof(AttributeRoutedController.AttributeRoutedAction), actionConstraint.Value); } [Fact] - public void GetDescriptors_WithRouteDataConstraint_WithBlockNonAttributedActions() + public void GetDescriptors_WithRouteValueAttribute() { // Arrange & Act var descriptors = GetDescriptors( typeof(HttpMethodController).GetTypeInfo(), - typeof(BlockNonAttributedActionsController).GetTypeInfo()).ToArray(); + typeof(RouteValueController).GetTypeInfo()).ToArray(); - var descriptorWithoutConstraint = Assert.Single( + var descriptorWithoutValue = Assert.Single( descriptors, - ad => ad.RouteConstraints.Any( - c => c.RouteKey == "key" && c.KeyHandling == RouteKeyHandling.DenyKey)); + ad => ad.RouteValues.Any(kvp => kvp.Key == "key" && string.IsNullOrEmpty(kvp.Value))); - var descriptorWithConstraint = Assert.Single( + var descriptorWithValue = Assert.Single( descriptors, - ad => ad.RouteConstraints.Any( - c => - c.KeyHandling == RouteKeyHandling.RequireKey && - c.RouteKey == "key" && - c.RouteValue == "value")); + ad => ad.RouteValues.Any(kvp => kvp.Key == "key" && kvp.Value == "value")); // Assert Assert.Equal(2, descriptors.Length); - Assert.Equal(3, descriptorWithConstraint.RouteConstraints.Count); + Assert.Equal(3, descriptorWithValue.RouteValues.Count); Assert.Single( - descriptorWithConstraint.RouteConstraints, + descriptorWithValue.RouteValues, c => - c.RouteKey == "controller" && - c.RouteValue == "BlockNonAttributedActions"); + c.Key == "controller" && + c.Value == "RouteValue"); Assert.Single( - descriptorWithConstraint.RouteConstraints, + descriptorWithValue.RouteValues, c => - c.RouteKey == "action" && - c.RouteValue == "Edit"); + c.Key == "action" && + c.Value == "Edit"); Assert.Single( - descriptorWithConstraint.RouteConstraints, + descriptorWithValue.RouteValues, c => - c.RouteKey == "key" && - c.RouteValue == "value" && - c.KeyHandling == RouteKeyHandling.RequireKey); + c.Key == "key" && + c.Value == "value"); - Assert.Equal(3, descriptorWithoutConstraint.RouteConstraints.Count); + Assert.Equal(3, descriptorWithoutValue.RouteValues.Count); Assert.Single( - descriptorWithoutConstraint.RouteConstraints, + descriptorWithoutValue.RouteValues, c => - c.RouteKey == "controller" && - c.RouteValue == "HttpMethod"); + c.Key == "controller" && + c.Value == "HttpMethod"); Assert.Single( - descriptorWithoutConstraint.RouteConstraints, + descriptorWithoutValue.RouteValues, c => - c.RouteKey == "action" && - c.RouteValue == "OnlyPost"); + c.Key == "action" && + c.Value == "OnlyPost"); Assert.Single( - descriptorWithoutConstraint.RouteConstraints, + descriptorWithoutValue.RouteValues, c => - c.RouteKey == "key" && - c.RouteValue == string.Empty && - c.KeyHandling == RouteKeyHandling.DenyKey); - } - - [Fact] - public void GetDescriptors_WithRouteDataConstraint_WithoutBlockNonAttributedActions() - { - // Arrange & Act - var descriptors = GetDescriptors( - typeof(HttpMethodController).GetTypeInfo(), - typeof(DontBlockNonAttributedActionsController).GetTypeInfo()).ToArray(); - - var descriptorWithConstraint = Assert.Single( - descriptors, - ad => ad.RouteConstraints.Any( - c => - c.KeyHandling == RouteKeyHandling.RequireKey && - c.RouteKey == "key" && - c.RouteValue == "value")); - - var descriptorWithoutConstraint = Assert.Single( - descriptors, - ad => !ad.RouteConstraints.Any(c => c.RouteKey == "key")); - - // Assert - Assert.Equal(2, descriptors.Length); - - Assert.Equal(3, descriptorWithConstraint.RouteConstraints.Count); - Assert.Single( - descriptorWithConstraint.RouteConstraints, - c => - c.RouteKey == "controller" && - c.RouteValue == "DontBlockNonAttributedActions"); - Assert.Single( - descriptorWithConstraint.RouteConstraints, - c => - c.RouteKey == "action" && - c.RouteValue == "Create"); - Assert.Single( - descriptorWithConstraint.RouteConstraints, - c => - c.RouteKey == "key" && - c.RouteValue == "value" && - c.KeyHandling == RouteKeyHandling.RequireKey); - - Assert.Equal(2, descriptorWithoutConstraint.RouteConstraints.Count); - Assert.Single( - descriptorWithoutConstraint.RouteConstraints, - c => - c.RouteKey == "controller" && - c.RouteValue == "HttpMethod"); - Assert.Single( - descriptorWithoutConstraint.RouteConstraints, - c => - c.RouteKey == "action" && - c.RouteValue == "OnlyPost"); + c.Key == "key" && + c.Value == string.Empty); } [Fact] @@ -1002,10 +933,10 @@ namespace Microsoft.AspNetCore.Mvc.Internal foreach (var actionDescriptor in actionDescriptors.Where(ad => ad.AttributeRouteInfo == null)) { - Assert.Equal(6, actionDescriptor.RouteConstraints.Count); - var routeGroupConstraint = Assert.Single(actionDescriptor.RouteConstraints, - rc => rc.RouteKey.Equals(TreeRouter.RouteGroupKey)); - Assert.Equal(RouteKeyHandling.DenyKey, routeGroupConstraint.KeyHandling); + Assert.Equal(6, actionDescriptor.RouteValues.Count); + Assert.Single( + actionDescriptor.RouteValues, + kvp => kvp.Key.Equals(TreeRouter.RouteGroupKey) && string.IsNullOrEmpty(kvp.Value)); } } @@ -1026,11 +957,10 @@ namespace Microsoft.AspNetCore.Mvc.Internal var indexAction = Assert.Single(actionDescriptors, ad => ad.Name.Equals("Index")); - Assert.Equal(1, indexAction.RouteConstraints.Count); + Assert.Equal(1, indexAction.RouteValues.Count); - var routeGroupConstraint = Assert.Single(indexAction.RouteConstraints, rc => rc.RouteKey.Equals(TreeRouter.RouteGroupKey)); - Assert.Equal(RouteKeyHandling.RequireKey, routeGroupConstraint.KeyHandling); - Assert.NotNull(routeGroupConstraint.RouteValue); + var routeGroup = Assert.Single(indexAction.RouteValues, kvp => kvp.Key.Equals(TreeRouter.RouteGroupKey)); + Assert.NotNull(routeGroup.Value); Assert.Equal(5, indexAction.RouteValueDefaults.Count); @@ -1043,11 +973,11 @@ namespace Microsoft.AspNetCore.Mvc.Internal var areaDefault = Assert.Single(indexAction.RouteValueDefaults, rd => rd.Key.Equals("area", StringComparison.OrdinalIgnoreCase)); Assert.Equal("Home", areaDefault.Value); - var myRouteConstraintDefault = Assert.Single(indexAction.RouteValueDefaults, rd => rd.Key.Equals("key", StringComparison.OrdinalIgnoreCase)); - Assert.Null(myRouteConstraintDefault.Value); + var mvRouteValueDefault = Assert.Single(indexAction.RouteValueDefaults, rd => rd.Key.Equals("key", StringComparison.OrdinalIgnoreCase)); + Assert.Null(mvRouteValueDefault.Value); - var anotherRouteConstraint = Assert.Single(indexAction.RouteValueDefaults, rd => rd.Key.Equals("second", StringComparison.OrdinalIgnoreCase)); - Assert.Null(anotherRouteConstraint.Value); + var anotherRouteValue = Assert.Single(indexAction.RouteValueDefaults, rd => rd.Key.Equals("second", StringComparison.OrdinalIgnoreCase)); + Assert.Null(anotherRouteValue.Value); } [Fact] @@ -1076,9 +1006,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal var actions = provider.GetDescriptors().ToArray(); var groupIds = actions.Select( - a => a.RouteConstraints - .Where(rc => rc.RouteKey == TreeRouter.RouteGroupKey) - .Select(rc => rc.RouteValue) + a => a.RouteValues + .Where(kvp => kvp.Key == TreeRouter.RouteGroupKey) + .Select(kvp => kvp.Value) .Single()) .ToArray(); @@ -1625,38 +1555,30 @@ namespace Microsoft.AspNetCore.Mvc.Internal { } } - private class MyRouteConstraintAttribute : RouteConstraintAttribute + private class MyRouteValueAttribute : RouteValueAttribute { - public MyRouteConstraintAttribute(bool blockNonAttributedActions) - : base("key", "value", blockNonAttributedActions) + public MyRouteValueAttribute() + : base("key", "value") { } } - private class MySecondRouteConstraintAttribute : RouteConstraintAttribute + private class MySecondRouteValueAttribute : RouteValueAttribute { - public MySecondRouteConstraintAttribute(bool blockNonAttributedActions) - : base("second", "value", blockNonAttributedActions) + public MySecondRouteValueAttribute() + : base("second", "value") { } } - [MyRouteConstraint(blockNonAttributedActions: true)] - private class BlockNonAttributedActionsController + [MyRouteValue] + private class RouteValueController { public void Edit() { } } - [MyRouteConstraint(blockNonAttributedActions: false)] - private class DontBlockNonAttributedActionsController - { - public void Create() - { - } - } - private class MyFilterAttribute : Attribute, IFilterMetadata { public MyFilterAttribute(int value) @@ -1677,7 +1599,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal } [Route("api/Token/[key]/[controller]")] - [MyRouteConstraint(false)] + [MyRouteValue] private class TokenReplacementController { [HttpGet("stub/[action]")] @@ -1880,8 +1802,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal public void DifferentHttpMethods() { } } - [MyRouteConstraint(blockNonAttributedActions: true)] - [MySecondRouteConstraint(blockNonAttributedActions: true)] + [MyRouteValue] + [MySecondRouteValue] private class ConstrainedController { public void ConstrainedNonAttributedAction() { } diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/KnownRouteValueConstraintTests.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/KnownRouteValueConstraintTests.cs index 62be3ef852..84d5da610c 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/KnownRouteValueConstraintTests.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/KnownRouteValueConstraintTests.cs @@ -55,7 +55,7 @@ namespace Microsoft.AspNetCore.Mvc.Routing var actionDescriptor = CreateActionDescriptor("testArea", "testController", "testAction"); - actionDescriptor.RouteConstraints.Add(new RouteDataActionConstraint("randomKey", "testRandom")); + actionDescriptor.RouteValues.Add("randomKey", "testRandom"); var httpContext = GetHttpContext(actionDescriptor); var route = Mock.Of(); var values = new RouteValueDictionary() @@ -85,10 +85,11 @@ namespace Microsoft.AspNetCore.Mvc.Routing public void RouteValue_DoesNotExists_MatchFails(string keyName, RouteDirection direction) { // Arrange - var actionDescriptor = CreateActionDescriptor("testArea", - "testController", - "testAction"); - actionDescriptor.RouteConstraints.Add(new RouteDataActionConstraint("randomKey", "testRandom")); + var actionDescriptor = CreateActionDescriptor( + "testArea", + "testController", + "testAction"); + actionDescriptor.RouteValues.Add("randomKey", "testRandom"); var httpContext = GetHttpContext(actionDescriptor); var route = Mock.Of(); var values = new RouteValueDictionary() @@ -186,23 +187,11 @@ namespace Microsoft.AspNetCore.Mvc.Routing var actionDescriptor = new ActionDescriptor() { Name = string.Format("Area: {0}, Controller: {1}, Action: {2}", area, controller, action), - RouteConstraints = new List(), }; - actionDescriptor.RouteConstraints.Add( - area == null ? - new RouteDataActionConstraint("area", null) : - new RouteDataActionConstraint("area", area)); - - actionDescriptor.RouteConstraints.Add( - controller == null ? - new RouteDataActionConstraint("controller", null) : - new RouteDataActionConstraint("controller", controller)); - - actionDescriptor.RouteConstraints.Add( - action == null ? - new RouteDataActionConstraint("action", null) : - new RouteDataActionConstraint("action", action)); + actionDescriptor.RouteValues.Add("area", area); + actionDescriptor.RouteValues.Add("controller", controller); + actionDescriptor.RouteValues.Add("action", action); return actionDescriptor; } diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Test/RazorViewEngineTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Test/RazorViewEngineTest.cs index 14ca86cf39..13888ae2de 100644 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Test/RazorViewEngineTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Test/RazorViewEngineTest.cs @@ -1152,7 +1152,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test [Theory] // Looks in RouteValueDefaults [InlineData(true)] - // Looks in RouteConstraints + // Looks in RouteValues [InlineData(false)] public void FindPage_SelectsActionCaseInsensitively(bool isAttributeRouted) { @@ -1194,7 +1194,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test [Theory] // Looks in RouteValueDefaults [InlineData(true)] - // Looks in RouteConstraints + // Looks in RouteValues [InlineData(false)] public void FindPage_LooksForPages_UsingActionDescriptor_Controller(bool isAttributeRouted) { @@ -1232,7 +1232,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test [Theory] // Looks in RouteValueDefaults [InlineData(true)] - // Looks in RouteConstraints + // Looks in RouteValues [InlineData(false)] public void FindPage_LooksForPages_UsingActionDescriptor_Areas(bool isAttributeRouted) { @@ -1495,17 +1495,12 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test } [Fact] - public void GetNormalizedRouteValue_ReturnsValueFromRouteConstraints_IfKeyHandlingIsRequired() + public void GetNormalizedRouteValue_ReturnsValueFromRouteValues_IfKeyHandlingIsRequired() { // Arrange var key = "some-key"; - var actionDescriptor = new ActionDescriptor - { - RouteConstraints = new[] - { - new RouteDataActionConstraint(key, "Route-Value") - } - }; + var actionDescriptor = new ActionDescriptor(); + actionDescriptor.RouteValues.Add(key, "Route-Value"); var actionContext = new ActionContext { @@ -1523,17 +1518,12 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test } [Fact] - public void GetNormalizedRouteValue_ReturnsRouteValue_IfValueDoesNotMatchRouteConstraint() + public void GetNormalizedRouteValue_ReturnsRouteValue_IfValueDoesNotMatch() { // Arrange var key = "some-key"; - var actionDescriptor = new ActionDescriptor - { - RouteConstraints = new[] - { - new RouteDataActionConstraint(key, "different-value") - } - }; + var actionDescriptor = new ActionDescriptor(); + actionDescriptor.RouteValues.Add(key, "different-value"); var actionContext = new ActionContext { @@ -1551,17 +1541,12 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test } [Fact] - public void GetNormalizedRouteValue_ReturnsNull_IfRouteConstraintKeyHandlingIsDeny() + public void GetNormalizedRouteValue_ReturnsNonNormalizedValue_IfActionRouteValueIsNull() { // Arrange var key = "some-key"; - var actionDescriptor = new ActionDescriptor - { - RouteConstraints = new[] - { - new RouteDataActionConstraint(key, routeValue: string.Empty) - } - }; + var actionDescriptor = new ActionDescriptor(); + actionDescriptor.RouteValues.Add(key, null); var actionContext = new ActionContext { @@ -1575,7 +1560,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test var result = RazorViewEngine.GetNormalizedRouteValue(actionContext, key); // Assert - Assert.Null(result); + Assert.Equal("route-value", result); } [Fact] @@ -1762,13 +1747,12 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test } var actionDesciptor = new ActionDescriptor(); - actionDesciptor.RouteConstraints = new List(); return new ActionContext(httpContext, routeData, actionDesciptor); } private static ActionContext GetActionContextWithActionDescriptor( IDictionary routeValues, - IDictionary routesInActionDescriptor, + IDictionary actionRouteValues, bool isAttributeRouted) { var httpContext = new DefaultHttpContext(); @@ -1782,17 +1766,16 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test if (isAttributeRouted) { actionDescriptor.AttributeRouteInfo = new AttributeRouteInfo(); - foreach (var kvp in routesInActionDescriptor) + foreach (var kvp in actionRouteValues) { actionDescriptor.RouteValueDefaults.Add(kvp.Key, kvp.Value); } } else { - actionDescriptor.RouteConstraints = new List(); - foreach (var kvp in routesInActionDescriptor) + foreach (var kvp in actionRouteValues) { - actionDescriptor.RouteConstraints.Add(new RouteDataActionConstraint(kvp.Key, kvp.Value)); + actionDescriptor.RouteValues.Add(kvp.Key, kvp.Value); } } diff --git a/test/Microsoft.AspNetCore.Mvc.WebApiCompatShimTest/ApiControllerActionDiscoveryTest.cs b/test/Microsoft.AspNetCore.Mvc.WebApiCompatShimTest/ApiControllerActionDiscoveryTest.cs index a0f894e836..f8987b4315 100644 --- a/test/Microsoft.AspNetCore.Mvc.WebApiCompatShimTest/ApiControllerActionDiscoveryTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.WebApiCompatShimTest/ApiControllerActionDiscoveryTest.cs @@ -84,14 +84,14 @@ namespace System.Web.Http var action = Assert.Single( actions, - a => a.RouteConstraints.Any(rc => rc.RouteKey == "action" && rc.RouteValue == "GetAll")); + a => a.RouteValues.Any(rc => rc.Key == "action" && rc.Value == "GetAll")); Assert.Equal( new string[] { "GET" }, Assert.Single(action.ActionConstraints.OfType()).HttpMethods); action = Assert.Single( actions, - a => a.RouteConstraints.Any(rc => rc.RouteKey == "action" && rc.RouteValue == "")); + a => a.RouteValues.Any(rc => rc.Key == "action" && string.IsNullOrEmpty(rc.Value))); Assert.Equal( new string[] { "GET" }, Assert.Single(action.ActionConstraints.OfType()).HttpMethods); @@ -120,14 +120,14 @@ namespace System.Web.Http var action = Assert.Single( actions, - a => a.RouteConstraints.Any(rc => rc.RouteKey == "action" && rc.RouteValue == "Edit")); + a => a.RouteValues.Any(rc => rc.Key == "action" && rc.Value == "Edit")); Assert.Equal( new string[] { "POST" }, Assert.Single(action.ActionConstraints.OfType()).HttpMethods); action = Assert.Single( actions, - a => a.RouteConstraints.Any(rc => rc.RouteKey == "action" && rc.RouteValue == "")); + a => a.RouteValues.Any(rc => rc.Key == "action" && string.IsNullOrEmpty(rc.Value))); Assert.Equal( new string[] { "POST" }, Assert.Single(action.ActionConstraints.OfType()).HttpMethods); @@ -156,14 +156,14 @@ namespace System.Web.Http var action = Assert.Single( actions, - a => a.RouteConstraints.Any(rc => rc.RouteKey == "action" && rc.RouteValue == "Delete")); + a => a.RouteValues.Any(rc => rc.Key == "action" && rc.Value == "Delete")); Assert.Equal( new string[] { "PUT" }, Assert.Single(action.ActionConstraints.OfType()).HttpMethods); action = Assert.Single( actions, - a => a.RouteConstraints.Any(rc => rc.RouteKey == "action" && rc.RouteValue == "")); + a => a.RouteValues.Any(rc => rc.Key == "action" && string.IsNullOrEmpty(rc.Value))); Assert.Equal( new string[] { "PUT" }, Assert.Single(action.ActionConstraints.OfType()).HttpMethods); @@ -193,14 +193,14 @@ namespace System.Web.Http var action = Assert.Single( actions, - a => a.RouteConstraints.Any(rc => rc.RouteKey == "action" && rc.RouteValue == "GetOptions")); + a => a.RouteValues.Any(rc => rc.Key == "action" && rc.Value == "GetOptions")); Assert.Equal( new string[] { "OPTIONS" }, Assert.Single(action.ActionConstraints.OfType()).HttpMethods); action = Assert.Single( actions, - a => a.RouteConstraints.Any(rc => rc.RouteKey == "action" && rc.RouteValue == "")); + a => a.RouteValues.Any(rc => rc.Key == "action" && string.IsNullOrEmpty(rc.Value))); Assert.Equal( new string[] { "OPTIONS" }, Assert.Single(action.ActionConstraints.OfType()).HttpMethods); @@ -252,7 +252,7 @@ namespace System.Web.Http Assert.NotEmpty(actions); foreach (var action in actions) { - Assert.Single(action.RouteConstraints, c => c.RouteKey == "area" && c.RouteValue == "api"); + Assert.Single(action.RouteValues, c => c.Key == "area" && c.Value== "api"); } }