diff --git a/samples/RoutingSample.Web/Startup.cs b/samples/RoutingSample.Web/Startup.cs index 6c4891ebbb..9e04b33f59 100644 --- a/samples/RoutingSample.Web/Startup.cs +++ b/samples/RoutingSample.Web/Startup.cs @@ -45,11 +45,11 @@ namespace RoutingSample.Web routeBuilder.MapRoute("regexStringRoute", "api/rconstraint/{controller}", new { foo = "Bar" }, - new { controller = new RegexConstraint("^(my.*)$") }); + new { controller = new RegexRouteConstraint("^(my.*)$") }); routeBuilder.MapRoute("regexRoute", "api/r2constraint/{controller}", new { foo = "Bar2" }, - new { controller = new RegexConstraint(new Regex("^(my.*)$")) }); + new { controller = new RegexRouteConstraint(new Regex("^(my.*)$")) }); routeBuilder.MapRoute("parameterConstraintRoute", "api/{controller}/{*extra}", diff --git a/src/Microsoft.AspNet.Routing/Constraints/AlphaRouteConstraint.cs b/src/Microsoft.AspNet.Routing/Constraints/AlphaRouteConstraint.cs new file mode 100644 index 0000000000..c6b7672e0b --- /dev/null +++ b/src/Microsoft.AspNet.Routing/Constraints/AlphaRouteConstraint.cs @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNet.Routing.Constraints +{ + /// + /// Constrains a route parameter to contain only lowercase or uppercase letters A through Z in the English alphabet. + /// + public class AlphaRouteConstraint : RegexRouteConstraint + { + /// + /// Initializes a new instance of the class. + /// + public AlphaRouteConstraint() : base(@"^[a-z]*$") + { + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Routing/Constraints/BoolRouteConstraint.cs b/src/Microsoft.AspNet.Routing/Constraints/BoolRouteConstraint.cs new file mode 100644 index 0000000000..cc8eb6c967 --- /dev/null +++ b/src/Microsoft.AspNet.Routing/Constraints/BoolRouteConstraint.cs @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Globalization; +using Microsoft.AspNet.Http; + +namespace Microsoft.AspNet.Routing.Constraints +{ + /// + /// Constrains a route parameter to represent only Boolean values. + /// + public class BoolRouteConstraint : IRouteConstraint + { + /// + public bool Match([NotNull] HttpContext httpContext, + [NotNull] IRouter route, + [NotNull] string routeKey, + [NotNull] IDictionary values, + RouteDirection routeDirection) + { + object value; + if (values.TryGetValue(routeKey, out value) && value != null) + { + if (value is bool) + { + return true; + } + + bool result; + var valueString = Convert.ToString(value, CultureInfo.InvariantCulture); + return Boolean.TryParse(valueString, out result); + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Routing/Constraints/DateTimeRouteConstraint.cs b/src/Microsoft.AspNet.Routing/Constraints/DateTimeRouteConstraint.cs new file mode 100644 index 0000000000..93b6130dc5 --- /dev/null +++ b/src/Microsoft.AspNet.Routing/Constraints/DateTimeRouteConstraint.cs @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Globalization; +using Microsoft.AspNet.Http; + +namespace Microsoft.AspNet.Routing.Constraints +{ + /// + /// Constrains a route parameter to represent only values. + /// Supports date time formats represented by CultureInfo.DateTimeFormat for the CultureInfo.InvariantCulture. + /// + public class DateTimeRouteConstraint : IRouteConstraint + { + /// + public bool Match([NotNull] HttpContext httpContext, + [NotNull] IRouter route, + [NotNull] string routeKey, + [NotNull] IDictionary values, + RouteDirection routeDirection) + { + object value; + if (values.TryGetValue(routeKey, out value) && value != null) + { + if (value is DateTime) + { + return true; + } + + DateTime result; + var valueString = Convert.ToString(value, CultureInfo.InvariantCulture); + return DateTime.TryParse(valueString, CultureInfo.InvariantCulture, DateTimeStyles.None, out result); + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Routing/Constraints/DecimalRouteConstraint.cs b/src/Microsoft.AspNet.Routing/Constraints/DecimalRouteConstraint.cs new file mode 100644 index 0000000000..45382ed9dc --- /dev/null +++ b/src/Microsoft.AspNet.Routing/Constraints/DecimalRouteConstraint.cs @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Globalization; +using Microsoft.AspNet.Http; + +namespace Microsoft.AspNet.Routing.Constraints +{ + /// + /// Constrains a route parameter to represent only decimal values. + /// + public class DecimalRouteConstraint : IRouteConstraint + { + /// + public bool Match([NotNull] HttpContext httpContext, + [NotNull] IRouter route, + [NotNull] string routeKey, + [NotNull] IDictionary values, + RouteDirection routeDirection) + { + object value; + if (values.TryGetValue(routeKey, out value) && value != null) + { + if (value is decimal) + { + return true; + } + + decimal result; + var valueString = Convert.ToString(value, CultureInfo.InvariantCulture); + return Decimal.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out result); + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Routing/Constraints/DoubleRouteConstraint.cs b/src/Microsoft.AspNet.Routing/Constraints/DoubleRouteConstraint.cs new file mode 100644 index 0000000000..90e53666ea --- /dev/null +++ b/src/Microsoft.AspNet.Routing/Constraints/DoubleRouteConstraint.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Globalization; +using Microsoft.AspNet.Http; + +namespace Microsoft.AspNet.Routing.Constraints +{ + /// + /// Constrains a route parameter to represent only 64-bit floating-point values. + /// + public class DoubleRouteConstraint : IRouteConstraint + { + /// + public bool Match([NotNull] HttpContext httpContext, + [NotNull] IRouter route, + [NotNull] string routeKey, + [NotNull] IDictionary values, + RouteDirection routeDirection) + { + object value; + if (values.TryGetValue(routeKey, out value) && value != null) + { + if (value is double) + { + return true; + } + + double result; + var valueString = Convert.ToString(value, CultureInfo.InvariantCulture); + return Double.TryParse(valueString, + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out result); + } + return false; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Routing/Constraints/FloatRouteConstraint.cs b/src/Microsoft.AspNet.Routing/Constraints/FloatRouteConstraint.cs new file mode 100644 index 0000000000..276995c752 --- /dev/null +++ b/src/Microsoft.AspNet.Routing/Constraints/FloatRouteConstraint.cs @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Globalization; +using Microsoft.AspNet.Http; + +namespace Microsoft.AspNet.Routing.Constraints +{ + /// + /// Constrains a route parameter to represent only 32-bit floating-point values. + /// + public class FloatRouteConstraint : IRouteConstraint + { + /// + public bool Match([NotNull] HttpContext httpContext, + [NotNull] IRouter route, + [NotNull] string routeKey, + [NotNull] IDictionary values, + RouteDirection routeDirection) + { + object value; + if (values.TryGetValue(routeKey, out value) && value != null) + { + if (value is float) + { + return true; + } + + float result; + var valueString = Convert.ToString(value, CultureInfo.InvariantCulture); + return Single.TryParse(valueString, + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out result); + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Routing/Constraints/GuidRouteConstraint.cs b/src/Microsoft.AspNet.Routing/Constraints/GuidRouteConstraint.cs new file mode 100644 index 0000000000..d6bb7e6c8d --- /dev/null +++ b/src/Microsoft.AspNet.Routing/Constraints/GuidRouteConstraint.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Globalization; +using Microsoft.AspNet.Http; + +namespace Microsoft.AspNet.Routing.Constraints +{ + /// + /// Constrains a route parameter to represent only values. + /// Matches values specified in any of the five formats "N", "D", "B", "P", or "X", + /// supported by Guid.ToString(string) and Guid.ToString(String, IFormatProvider) methods. + /// + public class GuidRouteConstraint : IRouteConstraint + { + /// + public bool Match([NotNull] HttpContext httpContext, + [NotNull] IRouter route, + [NotNull] string routeKey, + [NotNull] IDictionary values, + RouteDirection routeDirection) + { + object value; + if (values.TryGetValue(routeKey, out value) && value != null) + { + if (value is Guid) + { + return true; + } + + Guid result; + var valueString = Convert.ToString(value, CultureInfo.InvariantCulture); + return Guid.TryParse(valueString, out result); + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Routing/Constraints/LengthRouteConstraint.cs b/src/Microsoft.AspNet.Routing/Constraints/LengthRouteConstraint.cs new file mode 100644 index 0000000000..1d41153d0b --- /dev/null +++ b/src/Microsoft.AspNet.Routing/Constraints/LengthRouteConstraint.cs @@ -0,0 +1,91 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Globalization; +using Microsoft.AspNet.Http; + +namespace Microsoft.AspNet.Routing.Constraints +{ + /// + /// Constrains a route parameter to be a string of a given length or within a given range of lengths. + /// + public class LengthRouteConstraint : IRouteConstraint + { + /// + /// Initializes a new instance of the class that constrains + /// a route parameter to be a string of a given length. + /// + /// The length of the route parameter. + public LengthRouteConstraint(int length) + { + if (length < 0) + { + var errorMessage = Resources.FormatArgumentMustBeGreaterThanOrEqualTo(0); + throw new ArgumentOutOfRangeException("length", length, errorMessage); + } + + MinLength = MaxLength = length; + } + + /// + /// Initializes a new instance of the class that constrains + /// a route parameter to be a string of a given length. + /// + /// The minimum length allowed for the route parameter. + /// The maximum length allowed for the route parameter. + public LengthRouteConstraint(int minLength, int maxLength) + { + if (minLength < 0) + { + var errorMessage = Resources.FormatArgumentMustBeGreaterThanOrEqualTo(0); + throw new ArgumentOutOfRangeException("minLength", minLength, errorMessage); + } + + if (maxLength < 0) + { + var errorMessage = Resources.FormatArgumentMustBeGreaterThanOrEqualTo(0); + throw new ArgumentOutOfRangeException("maxLength", maxLength, errorMessage); + } + + if (minLength > maxLength) + { + var errorMessage = + Resources.FormatRangeConstraint_MinShouldBeLessThanOrEqualToMax("minLength", "maxLength"); + throw new ArgumentOutOfRangeException("minLength", minLength, errorMessage); + } + + MinLength = minLength; + MaxLength = maxLength; + } + + /// + /// Gets the minimum length allowed for the route parameter. + /// + public int MinLength { get; private set; } + + /// + /// Gets the maximum length allowed for the route parameter. + /// + public int MaxLength { get; private set; } + + /// + public bool Match([NotNull] HttpContext httpContext, + [NotNull] IRouter route, + [NotNull] string routeKey, + [NotNull] IDictionary values, + RouteDirection routeDirection) + { + object value; + if (values.TryGetValue(routeKey, out value) && value != null) + { + var valueString = Convert.ToString(value, CultureInfo.InvariantCulture); + var length = valueString.Length; + return length >= MinLength && length <= MaxLength; + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Routing/Constraints/LongRouteConstraint.cs b/src/Microsoft.AspNet.Routing/Constraints/LongRouteConstraint.cs new file mode 100644 index 0000000000..7a357921df --- /dev/null +++ b/src/Microsoft.AspNet.Routing/Constraints/LongRouteConstraint.cs @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Globalization; +using Microsoft.AspNet.Http; + +namespace Microsoft.AspNet.Routing.Constraints +{ + /// + /// Constrains a route parameter to represent only 64-bit integer values. + /// + public class LongRouteConstraint : IRouteConstraint + { + /// + public bool Match([NotNull] HttpContext httpContext, + [NotNull] IRouter route, + [NotNull] string routeKey, + [NotNull] IDictionary values, + RouteDirection routeDirection) + { + object value; + if (values.TryGetValue(routeKey, out value) && value != null) + { + if (value is long) + { + return true; + } + + long result; + var valueString = Convert.ToString(value, CultureInfo.InvariantCulture); + return Int64.TryParse(valueString, NumberStyles.Integer, CultureInfo.InvariantCulture, out result); + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Routing/Constraints/MaxLengthRouteConstraint.cs b/src/Microsoft.AspNet.Routing/Constraints/MaxLengthRouteConstraint.cs new file mode 100644 index 0000000000..e0e9dd81f5 --- /dev/null +++ b/src/Microsoft.AspNet.Routing/Constraints/MaxLengthRouteConstraint.cs @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Globalization; +using Microsoft.AspNet.Http; + +namespace Microsoft.AspNet.Routing.Constraints +{ + /// + /// Constrains a route parameter to be a string with a maximum length. + /// + public class MaxLengthRouteConstraint : IRouteConstraint + { + /// + /// Initializes a new instance of the class. + /// + /// The maximum length allowed for the route parameter. + public MaxLengthRouteConstraint(int maxLength) + { + if (maxLength < 0) + { + var errorMessage = Resources.FormatArgumentMustBeGreaterThanOrEqualTo(0); + throw new ArgumentOutOfRangeException("maxLength", maxLength, errorMessage); + } + + MaxLength = maxLength; + } + + /// + /// Gets the maximum length allowed for the route parameter. + /// + public int MaxLength { get; private set; } + + /// + public bool Match([NotNull] HttpContext httpContext, + [NotNull] IRouter route, + [NotNull] string routeKey, + [NotNull] IDictionary values, + RouteDirection routeDirection) + { + object value; + if (values.TryGetValue(routeKey, out value) && value != null) + { + var valueString = Convert.ToString(value, CultureInfo.InvariantCulture); + return valueString.Length <= MaxLength; + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Routing/Constraints/MaxRouteConstraint.cs b/src/Microsoft.AspNet.Routing/Constraints/MaxRouteConstraint.cs new file mode 100644 index 0000000000..0100a81808 --- /dev/null +++ b/src/Microsoft.AspNet.Routing/Constraints/MaxRouteConstraint.cs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Globalization; +using Microsoft.AspNet.Http; + +namespace Microsoft.AspNet.Routing.Constraints +{ + /// + /// Constrains a route parameter to be an integer with a maximum value. + /// + public class MaxRouteConstraint : IRouteConstraint + { + /// + /// Initializes a new instance of the class. + /// + /// The maximum value allowed for the route parameter. + public MaxRouteConstraint(long max) + { + Max = max; + } + + /// + /// Gets the maximum allowed value of the route parameter. + /// + public long Max { get; private set; } + + /// + public bool Match([NotNull] HttpContext httpContext, + [NotNull] IRouter route, + [NotNull] string routeKey, + [NotNull] IDictionary values, + RouteDirection routeDirection) + { + object value; + if (values.TryGetValue(routeKey, out value) && value != null) + { + long longValue; + var valueString = Convert.ToString(value, CultureInfo.InvariantCulture); + if (Int64.TryParse(valueString, NumberStyles.Integer, CultureInfo.InvariantCulture, out longValue)) + { + return longValue <= Max; + } + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Routing/Constraints/MinLengthRouteConstraint.cs b/src/Microsoft.AspNet.Routing/Constraints/MinLengthRouteConstraint.cs new file mode 100644 index 0000000000..b729af9f05 --- /dev/null +++ b/src/Microsoft.AspNet.Routing/Constraints/MinLengthRouteConstraint.cs @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Globalization; +using Microsoft.AspNet.Http; + +namespace Microsoft.AspNet.Routing.Constraints +{ + /// + /// Constrains a route parameter to be a string with a minimum length. + /// + public class MinLengthRouteConstraint : IRouteConstraint + { + /// + /// Initializes a new instance of the class. + /// + /// The minimum length allowed for the route parameter. + public MinLengthRouteConstraint(int minLength) + { + if (minLength < 0) + { + var errorMessage = Resources.FormatArgumentMustBeGreaterThanOrEqualTo(0); + throw new ArgumentOutOfRangeException("minLength", minLength, errorMessage); + } + + MinLength = minLength; + } + + /// + /// Gets the minimum length allowed for the route parameter. + /// + public int MinLength { get; private set; } + + /// + public bool Match([NotNull] HttpContext httpContext, + [NotNull] IRouter route, + [NotNull] string routeKey, + [NotNull] IDictionary values, + RouteDirection routeDirection) + { + object value; + if (values.TryGetValue(routeKey, out value) && value != null) + { + var valueString = Convert.ToString(value, CultureInfo.InvariantCulture); + return valueString.Length >= MinLength; + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Routing/Constraints/MinRouteConstraint.cs b/src/Microsoft.AspNet.Routing/Constraints/MinRouteConstraint.cs new file mode 100644 index 0000000000..cc387cafb1 --- /dev/null +++ b/src/Microsoft.AspNet.Routing/Constraints/MinRouteConstraint.cs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Globalization; +using Microsoft.AspNet.Http; + +namespace Microsoft.AspNet.Routing.Constraints +{ + /// + /// Constrains a route parameter to be a long with a minimum value. + /// + public class MinRouteConstraint : IRouteConstraint + { + /// + /// Initializes a new instance of the class. + /// + /// The minimum value allowed for the route parameter. + public MinRouteConstraint(long min) + { + Min = min; + } + + /// + /// Gets the minimum allowed value of the route parameter. + /// + public long Min { get; private set; } + + /// + public bool Match([NotNull] HttpContext httpContext, + [NotNull] IRouter route, + [NotNull] string routeKey, + [NotNull] IDictionary values, + RouteDirection routeDirection) + { + object value; + if (values.TryGetValue(routeKey, out value) && value != null) + { + long longValue; + var valueString = Convert.ToString(value, CultureInfo.InvariantCulture); + if (Int64.TryParse(valueString, NumberStyles.Integer, CultureInfo.InvariantCulture, out longValue)) + { + return longValue >= Min; + } + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Routing/Constraints/RangeRouteConstraint.cs b/src/Microsoft.AspNet.Routing/Constraints/RangeRouteConstraint.cs new file mode 100644 index 0000000000..e5cda4c8d0 --- /dev/null +++ b/src/Microsoft.AspNet.Routing/Constraints/RangeRouteConstraint.cs @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Globalization; +using Microsoft.AspNet.Http; + +namespace Microsoft.AspNet.Routing.Constraints +{ + /// + /// Constraints a route parameter to be an integer within a given range of values. + /// + public class RangeRouteConstraint : IRouteConstraint + { + /// + /// Initializes a new instance of the class. + /// + /// The minimum value. + /// The maximum value. + /// The minimum value should be less than or equal to the maximum value. + public RangeRouteConstraint(long min, long max) + { + if (min > max) + { + var errorMessage = Resources.FormatRangeConstraint_MinShouldBeLessThanOrEqualToMax("min", "max"); + throw new ArgumentOutOfRangeException("min", min, errorMessage); + } + + Min = min; + Max = max; + } + + /// + /// Gets the minimum allowed value of the route parameter. + /// + public long Min { get; private set; } + + /// + /// Gets the maximum allowed value of the route parameter. + /// + public long Max { get; private set; } + + /// + public bool Match([NotNull] HttpContext httpContext, + [NotNull] IRouter route, + [NotNull] string routeKey, + [NotNull] IDictionary values, + RouteDirection routeDirection) + { + object value; + if (values.TryGetValue(routeKey, out value) && value != null) + { + long longValue; + var valueString = Convert.ToString(value, CultureInfo.InvariantCulture); + if (Int64.TryParse(valueString, NumberStyles.Integer, CultureInfo.InvariantCulture, out longValue)) + { + return longValue >= Min && longValue <= Max; + } + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Routing/Constraints/RegexConstraint.cs b/src/Microsoft.AspNet.Routing/Constraints/RegexRouteConstraint.cs similarity index 87% rename from src/Microsoft.AspNet.Routing/Constraints/RegexConstraint.cs rename to src/Microsoft.AspNet.Routing/Constraints/RegexRouteConstraint.cs index a49c66524b..49ec279dea 100644 --- a/src/Microsoft.AspNet.Routing/Constraints/RegexConstraint.cs +++ b/src/Microsoft.AspNet.Routing/Constraints/RegexRouteConstraint.cs @@ -9,14 +9,14 @@ using Microsoft.AspNet.Http; namespace Microsoft.AspNet.Routing.Constraints { - public class RegexConstraint : IRouteConstraint + public class RegexRouteConstraint : IRouteConstraint { - public RegexConstraint([NotNull] Regex regex) + public RegexRouteConstraint([NotNull] Regex regex) { Constraint = regex; } - public RegexConstraint([NotNull] string regexPattern) + public RegexRouteConstraint([NotNull] string regexPattern) { Constraint = new Regex(regexPattern, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); } diff --git a/src/Microsoft.AspNet.Routing/DefaultInlineConstraintResolver.cs b/src/Microsoft.AspNet.Routing/DefaultInlineConstraintResolver.cs index c0bd82ab83..c57e0746d5 100644 --- a/src/Microsoft.AspNet.Routing/DefaultInlineConstraintResolver.cs +++ b/src/Microsoft.AspNet.Routing/DefaultInlineConstraintResolver.cs @@ -102,7 +102,7 @@ namespace Microsoft.AspNet.Routing { throw new InvalidOperationException( Resources.FormatDefaultInlineConstraintResolver_CouldNotFindCtor( - constraintTypeInfo.Name, argumentString.Length)); + constraintTypeInfo.Name, arguments.Length)); } else if (constructorMatches == 1) { @@ -113,7 +113,7 @@ namespace Microsoft.AspNet.Routing { throw new InvalidOperationException( Resources.FormatDefaultInlineConstraintResolver_AmbiguousCtors( - constraintTypeInfo.Name, argumentString.Length)); + constraintTypeInfo.Name, arguments.Length)); } } diff --git a/src/Microsoft.AspNet.Routing/InlineRouteParameterParser.cs b/src/Microsoft.AspNet.Routing/InlineRouteParameterParser.cs index a3726c8e36..a76c808a88 100644 --- a/src/Microsoft.AspNet.Routing/InlineRouteParameterParser.cs +++ b/src/Microsoft.AspNet.Routing/InlineRouteParameterParser.cs @@ -78,18 +78,18 @@ namespace Microsoft.AspNet.Routing } private static IRouteConstraint GetInlineConstraint(Group constraintGroup, - IInlineConstraintResolver constraintResolver) + IInlineConstraintResolver _constraintResolver) { var parameterConstraints = new List(); foreach (Capture constraintCapture in constraintGroup.Captures) { var inlineConstraint = constraintCapture.Value; - var constraint = constraintResolver.ResolveConstraint(inlineConstraint); + var constraint = _constraintResolver.ResolveConstraint(inlineConstraint); if (constraint == null) { throw new InvalidOperationException( Resources.FormatInlineRouteParser_CouldNotResolveConstraint( - constraintResolver.GetType().Name, inlineConstraint)); + _constraintResolver.GetType().Name, inlineConstraint)); } parameterConstraints.Add(constraint); diff --git a/src/Microsoft.AspNet.Routing/Microsoft.AspNet.Routing.kproj b/src/Microsoft.AspNet.Routing/Microsoft.AspNet.Routing.kproj index bddc4bd9d9..386d4eb8cb 100644 --- a/src/Microsoft.AspNet.Routing/Microsoft.AspNet.Routing.kproj +++ b/src/Microsoft.AspNet.Routing/Microsoft.AspNet.Routing.kproj @@ -25,8 +25,22 @@ + + + + + + + + + + + + + + - + @@ -46,9 +60,9 @@ - + diff --git a/src/Microsoft.AspNet.Routing/Properties/Resources.Designer.cs b/src/Microsoft.AspNet.Routing/Properties/Resources.Designer.cs index d3acb37cd9..10d5e4bcaa 100644 --- a/src/Microsoft.AspNet.Routing/Properties/Resources.Designer.cs +++ b/src/Microsoft.AspNet.Routing/Properties/Resources.Designer.cs @@ -10,6 +10,38 @@ namespace Microsoft.AspNet.Routing private static readonly ResourceManager _resourceManager = new ResourceManager("Microsoft.AspNet.Routing.Resources", typeof(Resources).GetTypeInfo().Assembly); + /// + /// Value must be greater than or equal to {0}. + /// + internal static string ArgumentMustBeGreaterThanOrEqualTo + { + get { return GetString("ArgumentMustBeGreaterThanOrEqualTo"); } + } + + /// + /// Value must be greater than or equal to {0}. + /// + internal static string FormatArgumentMustBeGreaterThanOrEqualTo(object p0) + { + return string.Format(CultureInfo.CurrentCulture, GetString("ArgumentMustBeGreaterThanOrEqualTo"), p0); + } + + /// + /// The value for argument '{0}' should be less than or equal to the value for the argument '{1}'. + /// + internal static string RangeConstraint_MinShouldBeLessThanOrEqualToMax + { + get { return GetString("RangeConstraint_MinShouldBeLessThanOrEqualToMax"); } + } + + /// + /// The value for argument '{0}' should be less than or equal to the value for the argument '{1}'. + /// + internal static string FormatRangeConstraint_MinShouldBeLessThanOrEqualToMax(object p0, object p1) + { + return string.Format(CultureInfo.CurrentCulture, GetString("RangeConstraint_MinShouldBeLessThanOrEqualToMax"), p0, p1); + } + /// /// The '{0}' property of '{1}' must not be null. /// diff --git a/src/Microsoft.AspNet.Routing/Resources.resx b/src/Microsoft.AspNet.Routing/Resources.resx index cea33cad4e..364a133d65 100644 --- a/src/Microsoft.AspNet.Routing/Resources.resx +++ b/src/Microsoft.AspNet.Routing/Resources.resx @@ -117,6 +117,12 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + Value must be greater than or equal to {0}. + + + The value for argument '{0}' should be less than or equal to the value for the argument '{1}'. + The '{0}' property of '{1}' must not be null. diff --git a/src/Microsoft.AspNet.Routing/RouteConstraintBuilder.cs b/src/Microsoft.AspNet.Routing/RouteConstraintBuilder.cs index 97e89da806..fb0db9834b 100644 --- a/src/Microsoft.AspNet.Routing/RouteConstraintBuilder.cs +++ b/src/Microsoft.AspNet.Routing/RouteConstraintBuilder.cs @@ -58,7 +58,7 @@ namespace Microsoft.AspNet.Routing var constraintsRegEx = "^(" + regexPattern + ")$"; - constraint = new RegexConstraint(constraintsRegEx); + constraint = new RegexRouteConstraint(constraintsRegEx); } constraints.Add(kvp.Key, constraint); diff --git a/src/Microsoft.AspNet.Routing/RouteOptions.cs b/src/Microsoft.AspNet.Routing/RouteOptions.cs index 2086e32d24..227f4d74aa 100644 --- a/src/Microsoft.AspNet.Routing/RouteOptions.cs +++ b/src/Microsoft.AspNet.Routing/RouteOptions.cs @@ -36,6 +36,26 @@ namespace Microsoft.AspNet.Routing { // Type-specific constraints { "int", typeof(IntRouteConstraint) }, + { "bool", typeof(BoolRouteConstraint) }, + { "datetime", typeof(DateTimeRouteConstraint) }, + { "decimal", typeof(DecimalRouteConstraint) }, + { "double", typeof(DoubleRouteConstraint) }, + { "float", typeof(FloatRouteConstraint) }, + { "guid", typeof(GuidRouteConstraint) }, + { "long", typeof(LongRouteConstraint) }, + + // Length constraints + { "minlength", typeof(MinLengthRouteConstraint) }, + { "maxlength", typeof(MaxLengthRouteConstraint) }, + { "length", typeof(LengthRouteConstraint) }, + + // Min/Max value constraints + { "min", typeof(MinRouteConstraint) }, + { "max", typeof(MaxRouteConstraint) }, + { "range", typeof(RangeRouteConstraint) }, + + // Regex-based constraints + { "alpha", typeof(AlphaRouteConstraint) }, }; } } diff --git a/test/Microsoft.AspNet.Routing.Tests/Constraints/AlphaRouteConstraintTests.cs b/test/Microsoft.AspNet.Routing.Tests/Constraints/AlphaRouteConstraintTests.cs new file mode 100644 index 0000000000..d10772fc1c --- /dev/null +++ b/test/Microsoft.AspNet.Routing.Tests/Constraints/AlphaRouteConstraintTests.cs @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#if NET45 + +using Microsoft.AspNet.Routing.Constraints; +using Xunit; + +namespace Microsoft.AspNet.Routing.Tests +{ + public class AlphaRouteConstraintTests + { + [Theory] + [InlineData("alpha", true)] + [InlineData("a1pha", false)] + [InlineData("ALPHA", true)] + [InlineData("A1PHA", false)] + [InlineData("alPHA", true)] + [InlineData("A1pHA", false)] + [InlineData("AlpHA╥", false)] + [InlineData("", true)] + public void AlphaRouteConstraintTest(string parameterValue, bool expected) + { + // Arrange + var constraint = new AlphaRouteConstraint(); + + // Act + var actual = ConstraintsTestHelper.TestConstraint(constraint, parameterValue); + + // Assert + Assert.Equal(expected, actual); + } + } +} + +#endif \ No newline at end of file diff --git a/test/Microsoft.AspNet.Routing.Tests/Constraints/BoolRouteConstraintTests.cs b/test/Microsoft.AspNet.Routing.Tests/Constraints/BoolRouteConstraintTests.cs new file mode 100644 index 0000000000..d4212fa2c6 --- /dev/null +++ b/test/Microsoft.AspNet.Routing.Tests/Constraints/BoolRouteConstraintTests.cs @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#if NET45 + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using Microsoft.AspNet.Http; +using Microsoft.AspNet.Routing.Constraints; +using Moq; +using Xunit; + +namespace Microsoft.AspNet.Routing.Tests +{ + public class BoolRouteConstraintTests + { + [Theory] + [InlineData("true", true)] + [InlineData("TruE", true)] + [InlineData("false", true)] + [InlineData("FalSe", true)] + [InlineData(" FalSe", true)] + [InlineData("True ", true)] + [InlineData(" False ", true)] + [InlineData(true, true)] + [InlineData(false, true)] + [InlineData(1, false)] + [InlineData("not-parseable-as-bool", false)] + public void BoolRouteConstraint(object parameterValue, bool expected) + { + // Arrange + var constraint = new BoolRouteConstraint(); + + // Act + var actual = ConstraintsTestHelper.TestConstraint(constraint, parameterValue); + + // Assert + Assert.Equal(expected, actual); + } + } +} + +#endif \ No newline at end of file diff --git a/test/Microsoft.AspNet.Routing.Tests/RouteConstraintsTests.cs b/test/Microsoft.AspNet.Routing.Tests/Constraints/CompositeRouteConstraintTests.cs similarity index 52% rename from test/Microsoft.AspNet.Routing.Tests/RouteConstraintsTests.cs rename to test/Microsoft.AspNet.Routing.Tests/Constraints/CompositeRouteConstraintTests.cs index 3f1fa3f2ec..26300a1b28 100644 --- a/test/Microsoft.AspNet.Routing.Tests/RouteConstraintsTests.cs +++ b/test/Microsoft.AspNet.Routing.Tests/Constraints/CompositeRouteConstraintTests.cs @@ -7,38 +7,19 @@ using System; using System.Collections.Generic; using System.Linq.Expressions; using Microsoft.AspNet.Http; -using Microsoft.AspNet.Routing.Constraints; using Moq; using Xunit; namespace Microsoft.AspNet.Routing.Tests { - public class RouteConstraintsTests + public class CompositeRouteConstraintTests { - [Theory] - [InlineData(42, true)] - [InlineData("42", true)] - [InlineData(3.14, false)] - [InlineData("43.567", false)] - [InlineData("42a", false)] - public void IntRouteConstraint_Match_AppliesConstraint(object parameterValue, bool expected) - { - // Arrange - var constraint = new IntRouteConstraint(); - - // Act - var actual = TestValue(constraint, parameterValue); - - // Assert - Assert.Equal(expected, actual); - } - [Theory] [InlineData(true, true, true)] [InlineData(true, false, false)] [InlineData(false, true, false)] [InlineData(false, false, false)] - public void CompoundRouteConstraint_Match_CallsMatchOnInnerConstraints(bool inner1Result, + public void CompositeRouteConstraint_Match_CallsMatchOnInnerConstraints(bool inner1Result, bool inner2Result, bool expected) { @@ -48,7 +29,7 @@ namespace Microsoft.AspNet.Routing.Tests // Act var constraint = new CompositeRouteConstraint(new[] { inner1.Object, inner2.Object }); - var actual = TestValue(constraint, null); + var actual = ConstraintsTestHelper.TestConstraint(constraint, null); // Assert Assert.Equal(expected, actual); @@ -69,28 +50,6 @@ namespace Microsoft.AspNet.Routing.Tests .Verifiable(); return mock; } - - private static void AssertMatchWasCalled(Mock mock, Times times) - { - mock.Verify(ConstraintMatchMethodExpression, times); - } - - private static bool TestValue(IRouteConstraint constraint, object value, Action routeConfig = null) - { - var context = new Mock(); - - IRouter route = new RouteCollection(); - - if (routeConfig != null) - { - routeConfig(route); - } - - var parameterName = "fake"; - var values = new Dictionary() { { parameterName, value } }; - var routeDirection = RouteDirection.IncomingRequest; - return constraint.Match(context.Object, route, parameterName, values, routeDirection); - } } } diff --git a/test/Microsoft.AspNet.Routing.Tests/Constraints/ConstraintsTestHelper.cs b/test/Microsoft.AspNet.Routing.Tests/Constraints/ConstraintsTestHelper.cs new file mode 100644 index 0000000000..e59bf2b070 --- /dev/null +++ b/test/Microsoft.AspNet.Routing.Tests/Constraints/ConstraintsTestHelper.cs @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#if NET45 + +using System; +using System.Collections.Generic; +using Microsoft.AspNet.Http; +using Moq; + +namespace Microsoft.AspNet.Routing.Tests +{ + public class ConstraintsTestHelper + { + public static bool TestConstraint(IRouteConstraint constraint, object value, Action routeConfig = null) + { + var context = new Mock(); + + var route = new RouteCollection(); + + if (routeConfig != null) + { + routeConfig(route); + } + + var parameterName = "fake"; + var values = new Dictionary() { { parameterName, value } }; + var routeDirection = RouteDirection.IncomingRequest; + return constraint.Match(context.Object, route, parameterName, values, routeDirection); + } + } +} + +#endif \ No newline at end of file diff --git a/test/Microsoft.AspNet.Routing.Tests/Constraints/DateTimeRouteConstraintTests.cs b/test/Microsoft.AspNet.Routing.Tests/Constraints/DateTimeRouteConstraintTests.cs new file mode 100644 index 0000000000..8fda375f82 --- /dev/null +++ b/test/Microsoft.AspNet.Routing.Tests/Constraints/DateTimeRouteConstraintTests.cs @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#if NET45 + +using System; +using System.Collections.Generic; +using Microsoft.AspNet.Routing.Constraints; +using Xunit; + +namespace Microsoft.AspNet.Routing.Tests +{ + public class DateTimeRouteConstraintTests + { + public static IEnumerable GetDateTimeObject + { + get + { + yield return new object[] + { + DateTime.Now, + true + }; + } + } + + [Theory] + [InlineData("12/25/2009", true)] + [InlineData("25/12/2009 11:45:00 PM", false)] + [InlineData("25/12/2009", false)] + [InlineData("11:45:00 PM", true)] + [InlineData("11:45:00", true)] + [InlineData("11:45", true)] + [InlineData("11", false)] + [InlineData("", false)] + [InlineData("Apr 5 2009 11:45:00 PM", true)] + [InlineData("April 5 2009 11:45:00 PM", true)] + [InlineData("12/25/2009 11:45:00 PM", true)] + [InlineData("11:45:00 PM", true)] + [InlineData("2009-05-12T11:45:00Z", true)] + [InlineData("not-parseable-as-date", false)] + [InlineData(false, false)] + [MemberData("GetDateTimeObject")] + public void DateTimeRouteConstraint(object parameterValue, bool expected) + { + // Arrange + var constraint = new DateTimeRouteConstraint(); + + // Act + var actual = ConstraintsTestHelper.TestConstraint(constraint, parameterValue); + + // Assert + Assert.Equal(expected, actual); + } + } +} + +#endif \ No newline at end of file diff --git a/test/Microsoft.AspNet.Routing.Tests/Constraints/DecimalRouteConstraintTests.cs b/test/Microsoft.AspNet.Routing.Tests/Constraints/DecimalRouteConstraintTests.cs new file mode 100644 index 0000000000..a3fd651194 --- /dev/null +++ b/test/Microsoft.AspNet.Routing.Tests/Constraints/DecimalRouteConstraintTests.cs @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#if NET45 + +using System.Collections.Generic; +using Microsoft.AspNet.Routing.Constraints; +using Xunit; + +namespace Microsoft.AspNet.Routing.Tests +{ + public class DecimalRouteConstraintTests + { + public static IEnumerable GetDecimalObject + { + get + { + yield return new object[] + { + 2m, + true + }; + } + } + + [Theory] + [InlineData("3.14", true)] + [InlineData("9223372036854775808.9223372036854775808", true)] + [InlineData("1.79769313486232E+300", false)] + [InlineData("not-parseable-as-decimal", false)] + [InlineData(false, false)] + [MemberData("GetDecimalObject")] + public void DecimalRouteConstraint_ApplyConstraint(object parameterValue, bool expected) + { + // Arrange + var constraint = new DecimalRouteConstraint(); + + // Act + var actual = ConstraintsTestHelper.TestConstraint(constraint, parameterValue); + + // Assert + Assert.Equal(expected, actual); + } + } +} + +#endif \ No newline at end of file diff --git a/test/Microsoft.AspNet.Routing.Tests/Constraints/DoubleRouteConstraintTests.cs b/test/Microsoft.AspNet.Routing.Tests/Constraints/DoubleRouteConstraintTests.cs new file mode 100644 index 0000000000..a0324f890d --- /dev/null +++ b/test/Microsoft.AspNet.Routing.Tests/Constraints/DoubleRouteConstraintTests.cs @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#if NET45 + +using Microsoft.AspNet.Routing.Constraints; +using Xunit; + +namespace Microsoft.AspNet.Routing.Tests +{ + public class DoubleRouteConstraintTests + { + [Theory] + [InlineData("3.14", true)] + [InlineData(3.14f, true)] + [InlineData(3.14d, true)] + [InlineData("1.79769313486232E+300", true)] + [InlineData("not-parseable-as-double", false)] + [InlineData(false, false)] + public void DoubleRouteConstraint_ApplyConstraint(object parameterValue, bool expected) + { + // Arrange + var constraint = new DoubleRouteConstraint(); + + // Act + var actual = ConstraintsTestHelper.TestConstraint(constraint, parameterValue); + + // Assert + Assert.Equal(expected, actual); + } + } +} + +#endif \ No newline at end of file diff --git a/test/Microsoft.AspNet.Routing.Tests/Constraints/FloatRouteConstraintTests.cs b/test/Microsoft.AspNet.Routing.Tests/Constraints/FloatRouteConstraintTests.cs new file mode 100644 index 0000000000..5f1545e1b0 --- /dev/null +++ b/test/Microsoft.AspNet.Routing.Tests/Constraints/FloatRouteConstraintTests.cs @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#if NET45 + +using Microsoft.AspNet.Routing.Constraints; +using Xunit; + +namespace Microsoft.AspNet.Routing.Tests +{ + public class FloatRouteConstraintTests + { + [Theory] + [InlineData("3.14", true)] + [InlineData(3.14, true)] + [InlineData("not-parseable-as-float", false)] + [InlineData(false, false)] + [InlineData("1.79769313486232E+300", false)] + public void FloatRouteConstraint_ApplyConstraint(object parameterValue, bool expected) + { + // Arrange + var constraint = new FloatRouteConstraint(); + + // Act + var actual = ConstraintsTestHelper.TestConstraint(constraint, parameterValue); + + // Assert + Assert.Equal(expected, actual); + } + } +} + +#endif \ No newline at end of file diff --git a/test/Microsoft.AspNet.Routing.Tests/Constraints/GuidRouteConstraintTests.cs b/test/Microsoft.AspNet.Routing.Tests/Constraints/GuidRouteConstraintTests.cs new file mode 100644 index 0000000000..b18c5b4957 --- /dev/null +++ b/test/Microsoft.AspNet.Routing.Tests/Constraints/GuidRouteConstraintTests.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#if NET45 + +using System; +using System.Collections.Generic; +using Microsoft.AspNet.Routing.Constraints; +using Xunit; + +namespace Microsoft.AspNet.Routing.Tests +{ + public class GuidRouteConstraintTests + { + [Theory] + [InlineData("12345678-1234-1234-1234-123456789012", false, true)] + [InlineData("12345678-1234-1234-1234-123456789012", true, true)] + [InlineData("12345678901234567890123456789012", false, true)] + [InlineData("not-parseable-as-guid", false, false)] + [InlineData(12, false, false)] + + public void GuidRouteConstraint_ApplyConstraint(object parameterValue, bool parseBeforeTest, bool expected) + { + // Arrange + if (parseBeforeTest) + { + parameterValue = Guid.Parse(parameterValue.ToString()); + } + + var constraint = new GuidRouteConstraint(); + + // Act + var actual = ConstraintsTestHelper.TestConstraint(constraint, parameterValue); + + // Assert + Assert.Equal(expected, actual); + } + } +} + +#endif \ No newline at end of file diff --git a/test/Microsoft.AspNet.Routing.Tests/Constraints/IntRouteConstraintsTests.cs b/test/Microsoft.AspNet.Routing.Tests/Constraints/IntRouteConstraintsTests.cs new file mode 100644 index 0000000000..12a6863df1 --- /dev/null +++ b/test/Microsoft.AspNet.Routing.Tests/Constraints/IntRouteConstraintsTests.cs @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#if NET45 + +using Microsoft.AspNet.Routing.Constraints; +using Xunit; + +namespace Microsoft.AspNet.Routing.Tests +{ + public class IntRouteConstraintsTests + { + [Theory] + [InlineData(42, true)] + [InlineData("42", true)] + [InlineData(3.14, false)] + [InlineData("43.567", false)] + [InlineData("42a", false)] + public void IntRouteConstraint_Match_AppliesConstraint(object parameterValue, bool expected) + { + // Arrange + var constraint = new IntRouteConstraint(); + + // Act + var actual = ConstraintsTestHelper.TestConstraint(constraint, parameterValue); + + // Assert + Assert.Equal(expected, actual); + } + } +} + +#endif \ No newline at end of file diff --git a/test/Microsoft.AspNet.Routing.Tests/Constraints/LengthRouteConstraintTests.cs b/test/Microsoft.AspNet.Routing.Tests/Constraints/LengthRouteConstraintTests.cs new file mode 100644 index 0000000000..4df1584191 --- /dev/null +++ b/test/Microsoft.AspNet.Routing.Tests/Constraints/LengthRouteConstraintTests.cs @@ -0,0 +1,90 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#if NET45 + +using System; +using Microsoft.AspNet.Routing.Constraints; +using Xunit; + +namespace Microsoft.AspNet.Routing.Tests +{ + public class LengthRouteConstraintTests + { + [Theory] + [InlineData(3, "123", true)] + [InlineData(3, "1234", false)] + [InlineData(0, "", true)] + public void LengthRouteConstraint_ExactLength_Tests(int length, string parameterValue, bool expected) + { + // Arrange + var constraint = new LengthRouteConstraint(length); + + // Act + var actual = ConstraintsTestHelper.TestConstraint(constraint, parameterValue); + + // Assert + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(3, 5, "12", false)] + [InlineData(3, 5, "123", true)] + [InlineData(3, 5, "1234", true)] + [InlineData(3, 5, "12345", true)] + [InlineData(3, 5, "123456", false)] + public void LengthRouteConstraint_Range_Tests(int min, int max, string parameterValue, bool expected) + { + // Arrange + var constraint = new LengthRouteConstraint(min, max); + + // Act + var actual = ConstraintsTestHelper.TestConstraint(constraint, parameterValue); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void LengthRouteConstraint_SettingLengthLessThanZero_Throws() + { + // Act & Assert + var ex = Assert.Throws(() => new LengthRouteConstraint(-1)); + Assert.Equal("Value must be greater than or equal to 0.\r\nParameter name: length\r\n" + + "Actual value was -1.", + ex.Message); + } + + [Fact] + public void LengthRouteConstraint_SettingMinLengthLessThanZero_Throws() + { + // Act & Assert + var ex = Assert.Throws(() => new LengthRouteConstraint(-1, 3)); + Assert.Equal("Value must be greater than or equal to 0.\r\nParameter name: minLength\r\n"+ + "Actual value was -1.", + ex.Message); + } + + [Fact] + public void LengthRouteConstraint_SettingMaxLengthLessThanZero_Throws() + { + // Act & Assert + var ex = Assert.Throws(() => new LengthRouteConstraint(0, -1)); + Assert.Equal("Value must be greater than or equal to 0.\r\nParameter name: maxLength\r\n" + + "Actual value was -1.", + ex.Message); + } + + [Fact] + public void LengthRouteConstraint_MinGreaterThanMax_Throws() + { + // Arrange Act & Assert + var ex = Assert.Throws(() => new LengthRouteConstraint(3, 2)); + Assert.Equal("The value for argument 'minLength' should be less than or equal to the "+ + "value for the argument 'maxLength'.\r\nParameter name: minLength\r\nActual value was 3.", + ex.Message); + } + } +} + +#endif \ No newline at end of file diff --git a/test/Microsoft.AspNet.Routing.Tests/Constraints/LongRouteConstraintTests.cs b/test/Microsoft.AspNet.Routing.Tests/Constraints/LongRouteConstraintTests.cs new file mode 100644 index 0000000000..98266fb71b --- /dev/null +++ b/test/Microsoft.AspNet.Routing.Tests/Constraints/LongRouteConstraintTests.cs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#if NET45 + +using Microsoft.AspNet.Routing.Constraints; +using Xunit; + +namespace Microsoft.AspNet.Routing.Tests +{ + public class LongRouteConstraintTests + { + [Theory] + [InlineData(42, true)] + [InlineData(42L, true)] + [InlineData("42", true)] + [InlineData("9223372036854775807", true)] + [InlineData(3.14, false)] + [InlineData("43.567", false)] + [InlineData("42a", false)] + public void LongRouteConstraintTest(object parameterValue, bool expected) + { + // Arrange + var constraint = new LongRouteConstraint(); + + // Act + var actual = ConstraintsTestHelper.TestConstraint(constraint, parameterValue); + + // Assert + Assert.Equal(expected, actual); + } + } +} + +#endif \ No newline at end of file diff --git a/test/Microsoft.AspNet.Routing.Tests/Constraints/MaxLengthRouteConstraintTests.cs b/test/Microsoft.AspNet.Routing.Tests/Constraints/MaxLengthRouteConstraintTests.cs new file mode 100644 index 0000000000..ae318d1221 --- /dev/null +++ b/test/Microsoft.AspNet.Routing.Tests/Constraints/MaxLengthRouteConstraintTests.cs @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#if NET45 + +using System; +using Microsoft.AspNet.Routing.Constraints; +using Xunit; + +namespace Microsoft.AspNet.Routing.Tests +{ + public class MaxLengthRouteConstraintTests + { + [Theory] + [InlineData(3, "", true)] + [InlineData(3, "12", true)] + [InlineData(3, "123", true)] + [InlineData(3, "1234", false)] + public void MaxLengthRouteConstraint_ApplyConstraint(int min, string parameterValue, bool expected) + { + // Arrange + var constraint = new MaxLengthRouteConstraint(min); + + // Act + var actual = ConstraintsTestHelper.TestConstraint(constraint, parameterValue); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void MaxLengthRouteConstraint_SettingMaxLengthLessThanZero_Throws() + { + // Act & Assert + var ex = Assert.Throws(()=> new MaxLengthRouteConstraint(-1)); + Assert.Equal("Value must be greater than or equal to 0.\r\nParameter name: maxLength\r\n" + + "Actual value was -1.", + ex.Message); + } + } +} + +#endif \ No newline at end of file diff --git a/test/Microsoft.AspNet.Routing.Tests/Constraints/MaxRouteConstraintTests.cs b/test/Microsoft.AspNet.Routing.Tests/Constraints/MaxRouteConstraintTests.cs new file mode 100644 index 0000000000..9ca9d5518e --- /dev/null +++ b/test/Microsoft.AspNet.Routing.Tests/Constraints/MaxRouteConstraintTests.cs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#if NET45 + +using Microsoft.AspNet.Routing.Constraints; +using Xunit; + +namespace Microsoft.AspNet.Routing.Tests +{ + public class MaxRouteConstraintTests + { + [Theory] + [InlineData(3, 2, true)] + [InlineData(3, 3, true)] + [InlineData(3, 4, false)] + public void MaxRouteConstraint_ApplyConstraint(long max, int parameterValue, bool expected) + { + // Arrange + var constraint = new MaxRouteConstraint(max); + + // Act + var actual = ConstraintsTestHelper.TestConstraint(constraint, parameterValue); + + // Assert + Assert.Equal(expected, actual); + } + } +} + +#endif \ No newline at end of file diff --git a/test/Microsoft.AspNet.Routing.Tests/Constraints/MinLengthRouteConstraintTests.cs b/test/Microsoft.AspNet.Routing.Tests/Constraints/MinLengthRouteConstraintTests.cs new file mode 100644 index 0000000000..6829bfb169 --- /dev/null +++ b/test/Microsoft.AspNet.Routing.Tests/Constraints/MinLengthRouteConstraintTests.cs @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#if NET45 + +using System; +using Microsoft.AspNet.Routing.Constraints; +using Xunit; + +namespace Microsoft.AspNet.Routing.Tests +{ + public class MinLengthRouteConstraintTests + { + [Theory] + [InlineData(3, "1234", true)] + [InlineData(3, "123", true)] + [InlineData(3, "12", false)] + [InlineData(3, "", false)] + public void MinLengthRouteConstraint_ApplyConstraint(int min, string parameterValue, bool expected) + { + // Arrange + var constraint = new MinLengthRouteConstraint(min); + + // Act + var actual = ConstraintsTestHelper.TestConstraint(constraint, parameterValue); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void MinLengthRouteConstraint_SettingMinLengthLessThanZero_Throws() + { + // Act & Assert + var ex = Assert.Throws(() => new MinLengthRouteConstraint(-1)); + Assert.Equal("Value must be greater than or equal to 0.\r\nParameter name: minLength\r\n" + + "Actual value was -1.", + ex.Message); + } + } +} + +#endif \ No newline at end of file diff --git a/test/Microsoft.AspNet.Routing.Tests/Constraints/MinRouteConstraintTests.cs b/test/Microsoft.AspNet.Routing.Tests/Constraints/MinRouteConstraintTests.cs new file mode 100644 index 0000000000..6ee191fbb1 --- /dev/null +++ b/test/Microsoft.AspNet.Routing.Tests/Constraints/MinRouteConstraintTests.cs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#if NET45 + +using Microsoft.AspNet.Routing.Constraints; +using Xunit; + +namespace Microsoft.AspNet.Routing.Tests +{ + public class MinRouteConstraintTests + { + [Theory] + [InlineData(3, 4, true)] + [InlineData(3, 3, true)] + [InlineData(3, 2, false)] + public void MinRouteConstraint_ApplyConstraint(long min, int parameterValue, bool expected) + { + // Arrange + var constraint = new MinRouteConstraint(min); + + // Act + var actual = ConstraintsTestHelper.TestConstraint(constraint, parameterValue); + + // Assert + Assert.Equal(expected, actual); + } + } +} + +#endif \ No newline at end of file diff --git a/test/Microsoft.AspNet.Routing.Tests/Constraints/RangeRouteConstraintTests.cs b/test/Microsoft.AspNet.Routing.Tests/Constraints/RangeRouteConstraintTests.cs new file mode 100644 index 0000000000..f1b1fe589e --- /dev/null +++ b/test/Microsoft.AspNet.Routing.Tests/Constraints/RangeRouteConstraintTests.cs @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#if NET45 + +using System; +using Microsoft.AspNet.Routing.Constraints; +using Xunit; + +namespace Microsoft.AspNet.Routing.Tests +{ + public class RangeRouteConstraintTests + { + [Theory] + [InlineData(long.MinValue, long.MaxValue, 2, true)] + [InlineData(3, 5, 3, true)] + [InlineData(3, 5, 4, true)] + [InlineData(3, 5, 5, true)] + [InlineData(3, 5, 6, false)] + [InlineData(3, 5, 2, false)] + [InlineData(3, 3, 2, false)] + [InlineData(3, 3, 3, true)] + public void RangeRouteConstraintTest_ValidValue_ApplyConstraint(long min, long max, int parameterValue, bool expected) + { + // Arrange + var constraint = new RangeRouteConstraint(min, max); + + // Act + var actual = ConstraintsTestHelper.TestConstraint(constraint, parameterValue); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void RangeRouteConstraint_MinGreaterThanMax_Throws() + { + // Arrange Act & Assert + var ex = Assert.Throws(() => new RangeRouteConstraint(3, 2)); + Assert.Equal("The value for argument 'min' should be less than or equal to the value for the argument "+ + "'max'.\r\nParameter name: min\r\nActual value was 3.", + ex.Message); + } + } +} + +#endif \ No newline at end of file diff --git a/test/Microsoft.AspNet.Routing.Tests/RegexConstraintTests.cs b/test/Microsoft.AspNet.Routing.Tests/Constraints/RegexConstraintTests.cs similarity index 91% rename from test/Microsoft.AspNet.Routing.Tests/RegexConstraintTests.cs rename to test/Microsoft.AspNet.Routing.Tests/Constraints/RegexConstraintTests.cs index a0e85ba860..0c67824e03 100644 --- a/test/Microsoft.AspNet.Routing.Tests/RegexConstraintTests.cs +++ b/test/Microsoft.AspNet.Routing.Tests/Constraints/RegexConstraintTests.cs @@ -27,7 +27,7 @@ namespace Microsoft.AspNet.Routing.Tests bool shouldMatch) { // Arrange - var constraint = new RegexConstraint(constraintValue); + var constraint = new RegexRouteConstraint(constraintValue); var values = new RouteValueDictionary(new {controller = routeValue}); // Assert @@ -38,7 +38,7 @@ namespace Microsoft.AspNet.Routing.Tests public void RegexConstraint_TakesRegexAsInput_SimpleMatch() { // Arrange - var constraint = new RegexConstraint(new Regex("^abc$")); + var constraint = new RegexRouteConstraint(new Regex("^abc$")); var values = new RouteValueDictionary(new { controller = "abc"}); // Assert @@ -49,7 +49,7 @@ namespace Microsoft.AspNet.Routing.Tests public void RegexConstraintConstructedWithRegex_SimpleFailedMatch() { // Arrange - var constraint = new RegexConstraint(new Regex("^abc$")); + var constraint = new RegexRouteConstraint(new Regex("^abc$")); var values = new RouteValueDictionary(new { controller = "Abc" }); // Assert @@ -60,7 +60,7 @@ namespace Microsoft.AspNet.Routing.Tests public void RegexConstraintFailsIfKeyIsNotFoundInRouteValues() { // Arrange - var constraint = new RegexConstraint(new Regex("^abc$")); + var constraint = new RegexRouteConstraint(new Regex("^abc$")); var values = new RouteValueDictionary(new { action = "abc" }); // Assert @@ -71,7 +71,7 @@ namespace Microsoft.AspNet.Routing.Tests public void RegexConstraintIsCultureInsensitiveWhenConstructredWithString() { // Arrange - var constraint = new RegexConstraint("^([a-z]+)$"); + var constraint = new RegexRouteConstraint("^([a-z]+)$"); var values = new RouteValueDictionary(new { controller = "\u0130" }); // Turkish upper-case dotted I var currentThread = Thread.CurrentThread; diff --git a/test/Microsoft.AspNet.Routing.Tests/ConstraintsBuilderTests.cs b/test/Microsoft.AspNet.Routing.Tests/ConstraintsBuilderTests.cs index 0f922d2d7d..0fd6a2636d 100644 --- a/test/Microsoft.AspNet.Routing.Tests/ConstraintsBuilderTests.cs +++ b/test/Microsoft.AspNet.Routing.Tests/ConstraintsBuilderTests.cs @@ -46,7 +46,7 @@ namespace Microsoft.AspNet.Routing.Tests var constraint = constraintDictionary["controller"]; - Assert.IsType(constraint); + Assert.IsType(constraint); } [Fact] diff --git a/test/Microsoft.AspNet.Routing.Tests/DefaultInlineConstraintResolverTest.cs b/test/Microsoft.AspNet.Routing.Tests/DefaultInlineConstraintResolverTest.cs index 1d387a81a3..07195cbd99 100644 --- a/test/Microsoft.AspNet.Routing.Tests/DefaultInlineConstraintResolverTest.cs +++ b/test/Microsoft.AspNet.Routing.Tests/DefaultInlineConstraintResolverTest.cs @@ -15,15 +15,18 @@ namespace Microsoft.AspNet.Routing.Tests { public class DefaultInlineConstraintResolverTest { + private IInlineConstraintResolver _constraintResolver; + + public DefaultInlineConstraintResolverTest() + { + var routeOptions = new RouteOptions(); + _constraintResolver = GetInlineConstraintResolver(routeOptions); + } [Fact] public void ResolveConstraint_IntConstraint_ResolvesCorrectly() { - // Arrange - var routeOptions = new RouteOptions(); - var constraintResolver = GetInlineConstraintResolver(routeOptions); - - // Act - var constraint = constraintResolver.ResolveConstraint("int"); + // Arrange & Act + var constraint = _constraintResolver.ResolveConstraint("int"); // Assert Assert.IsType(constraint); @@ -32,17 +35,190 @@ namespace Microsoft.AspNet.Routing.Tests [Fact] public void ResolveConstraint_IntConstraintWithArgument_Throws() { - // Arrange - var routeOptions = new RouteOptions(); - var constraintResolver = GetInlineConstraintResolver(routeOptions); - - // Act & Assert + // Arrange, Act & Assert var ex = Assert.Throws( - () => constraintResolver.ResolveConstraint("int(5)")); + () => _constraintResolver.ResolveConstraint("int(5)")); Assert.Equal("Could not find a constructor for constraint type 'IntRouteConstraint'"+ " with the following number of parameters: 1.", ex.Message); } + [Fact] + public void ResolveConstraint_AlphaConstraint() + { + // Arrange & Act + var constraint = _constraintResolver.ResolveConstraint("alpha"); + + // Assert + Assert.IsType(constraint); + } + + [Fact] + public void ResolveConstraint_BoolConstraint() + { + // Arrange & Act + var constraint = _constraintResolver.ResolveConstraint("bool"); + + // Assert + Assert.IsType(constraint); + } + + [Fact] + public void ResolveConstraint_CompositeConstraintIsNotRegistered() + { + // Arrange, Act & Assert + Assert.Null(_constraintResolver.ResolveConstraint("composite")); + } + + [Fact] + public void ResolveConstraint_DateTimeConstraint() + { + // Arrange & Act + var constraint = _constraintResolver.ResolveConstraint("datetime"); + + // Assert + Assert.IsType(constraint); + } + + [Fact] + public void ResolveConstraint_DecimalConstraint() + { + // Arrange & Act + var constraint = _constraintResolver.ResolveConstraint("decimal"); + + // Assert + Assert.IsType(constraint); + } + + [Fact] + public void ResolveConstraint_DoubleConstraint() + { + // Arrange & Act + var constraint = _constraintResolver.ResolveConstraint("double"); + + // Assert + Assert.IsType(constraint); + } + + [Fact] + public void ResolveConstraint_FloatConstraint() + { + // Arrange & Act + var constraint = _constraintResolver.ResolveConstraint("float"); + + // Assert + Assert.IsType(constraint); + } + + [Fact] + public void ResolveConstraint_GuidConstraint() + { + // Arrange & Act + var constraint = _constraintResolver.ResolveConstraint("guid"); + + // Assert + Assert.IsType(constraint); + } + + [Fact] + public void ResolveConstraint_IntConstraint() + { + // Arrange & Act + var constraint = _constraintResolver.ResolveConstraint("int"); + + // Assert + Assert.IsType(constraint); + } + + [Fact] + public void ResolveConstraint_LengthConstraint() + { + // Arrange & Act + var constraint = _constraintResolver.ResolveConstraint("length(5)"); + + // Assert + Assert.IsType(constraint); + Assert.Equal(5, ((LengthRouteConstraint)constraint).MinLength); + Assert.Equal(5, ((LengthRouteConstraint)constraint).MaxLength); + } + + [Fact] + public void ResolveConstraint_LengthRangeConstraint() + { + // Arrange & Act + var constraint = _constraintResolver.ResolveConstraint("length(5, 10)"); + + // Assert + var lengthConstraint = Assert.IsType(constraint); + Assert.Equal(5, lengthConstraint.MinLength); + Assert.Equal(10, lengthConstraint.MaxLength); + } + + [Fact] + public void ResolveConstraint_LongRangeConstraint() + { + // Arrange & Act + var constraint = _constraintResolver.ResolveConstraint("long"); + + // Assert + Assert.IsType(constraint); + } + + [Fact] + public void ResolveConstraint_MaxConstraint() + { + // Arrange & Act + var constraint = _constraintResolver.ResolveConstraint("max(10)"); + + // Assert + Assert.IsType(constraint); + Assert.Equal(10, ((MaxRouteConstraint)constraint).Max); + } + + [Fact] + public void ResolveConstraint_MaxLengthConstraint() + { + // Arrange & Act + var constraint = _constraintResolver.ResolveConstraint("maxlength(10)"); + + // Assert + Assert.IsType(constraint); + Assert.Equal(10, ((MaxLengthRouteConstraint)constraint).MaxLength); + } + + [Fact] + public void ResolveConstraint_MinConstraint() + { + // Arrange & Act + var constraint = _constraintResolver.ResolveConstraint("min(3)"); + + // Assert + Assert.IsType(constraint); + Assert.Equal(3, ((MinRouteConstraint)constraint).Min); + } + + [Fact] + public void ResolveConstraint_MinLengthConstraint() + { + // Arrange & Act + var constraint = _constraintResolver.ResolveConstraint("minlength(3)"); + + // Assert + Assert.IsType(constraint); + Assert.Equal(3, ((MinLengthRouteConstraint)constraint).MinLength); + } + + [Fact] + public void ResolveConstraint_RangeConstraint() + { + // Arrange & Act + var constraint = _constraintResolver.ResolveConstraint("range(5, 10)"); + + // Assert + Assert.IsType(constraint); + var rangeConstraint = (RangeRouteConstraint)constraint; + Assert.Equal(5, rangeConstraint.Min); + Assert.Equal(10, rangeConstraint.Max); + } [Fact] public void ResolveConstraint_SupportsCustomConstraints() @@ -74,6 +250,32 @@ namespace Microsoft.AspNet.Routing.Tests ex.Message); } + [Fact] + public void ResolveConstraint_AmbiguousConstructors_Throws() + { + // Arrange + var routeOptions = new RouteOptions(); + routeOptions.ConstraintMap.Add("custom", typeof(MultiConstructorRouteConstraint)); + var resolver = GetInlineConstraintResolver(routeOptions); + + // Act & Assert + var ex = Assert.Throws(() => resolver.ResolveConstraint("custom(5,6)")); + Assert.Equal("The constructor to use for activating the constraint type 'MultiConstructorRouteConstraint' is ambiguous." + + " Multiple constructors were found with the following number of parameters: 2.", + ex.Message); + } + + [Fact] + public void ResolveConstraint_NoMatchingConstructor_Throws() + { + // Arrange + // Act & Assert + var ex = Assert.Throws(() => _constraintResolver.ResolveConstraint("int(5,6)")); + Assert.Equal("Could not find a constructor for constraint type 'IntRouteConstraint'" + + " with the following number of parameters: 2.", + ex.Message); + } + private IInlineConstraintResolver GetInlineConstraintResolver(RouteOptions routeOptions) { var optionsAccessor = new Mock>(); @@ -84,6 +286,26 @@ namespace Microsoft.AspNet.Routing.Tests return new DefaultInlineConstraintResolver(serviceProvider.Object, optionsAccessor.Object); } + private class MultiConstructorRouteConstraint : IRouteConstraint + { + public MultiConstructorRouteConstraint(string pattern, int intArg) + { + } + + public MultiConstructorRouteConstraint(int intArg, string pattern) + { + } + + public bool Match(HttpContext httpContext, + IRouter route, + string routeKey, + IDictionary values, + RouteDirection routeDirection) + { + return true; + } + } + private class CustomRouteConstraint : IRouteConstraint { public CustomRouteConstraint(string pattern) diff --git a/test/Microsoft.AspNet.Routing.Tests/InlineRouteParameterParserTests.cs b/test/Microsoft.AspNet.Routing.Tests/InlineRouteParameterParserTests.cs index 28c22bc164..5f5ce1665a 100644 --- a/test/Microsoft.AspNet.Routing.Tests/InlineRouteParameterParserTests.cs +++ b/test/Microsoft.AspNet.Routing.Tests/InlineRouteParameterParserTests.cs @@ -176,6 +176,18 @@ namespace Microsoft.AspNet.Routing.Tests Assert.Equal(@"\w,\w", ((TestRouteConstraint)templatePart.InlineConstraint).Pattern); } + [Fact] + public void ParseRouteParameter_ConstraintWithEqualsFollowedByQuestionMark_PatternIsParsedCorrectly() + { + // Arrange & Act + var templatePart = ParseParameter(@"param:int=?"); + + // Assert + Assert.Equal("param", templatePart.Name); + var constraint = Assert.IsType(templatePart.InlineConstraint); + Assert.Equal("", templatePart.DefaultValue); + } + [Theory] [InlineData(",")] [InlineData("(")] @@ -270,15 +282,15 @@ namespace Microsoft.AspNet.Routing.Tests private TemplatePart ParseParameter(string routeParameter) { - var constraintResolver = GetConstraintResolver(); - var templatePart = InlineRouteParameterParser.ParseRouteParameter(routeParameter, constraintResolver); + var _constraintResolver = GetConstraintResolver(); + var templatePart = InlineRouteParameterParser.ParseRouteParameter(routeParameter, _constraintResolver); return templatePart; } private static Template.Template ParseRouteTemplate(string template) { - var constraintResolver = GetConstraintResolver(); - return TemplateParser.Parse(template, constraintResolver); + var _constraintResolver = GetConstraintResolver(); + return TemplateParser.Parse(template, _constraintResolver); } private static IInlineConstraintResolver GetConstraintResolver() diff --git a/test/Microsoft.AspNet.Routing.Tests/Microsoft.AspNet.Routing.Tests.kproj b/test/Microsoft.AspNet.Routing.Tests/Microsoft.AspNet.Routing.Tests.kproj index fadc051a54..7ab2d1633b 100644 --- a/test/Microsoft.AspNet.Routing.Tests/Microsoft.AspNet.Routing.Tests.kproj +++ b/test/Microsoft.AspNet.Routing.Tests/Microsoft.AspNet.Routing.Tests.kproj @@ -22,16 +22,35 @@ + + + + + + + + + + + + + + + + + + - - + + + - + \ No newline at end of file diff --git a/test/Microsoft.AspNet.Routing.Tests/Template/TemplateRouteTests.cs b/test/Microsoft.AspNet.Routing.Tests/Template/TemplateRouteTests.cs index a9bb187ac5..2409f3628e 100644 --- a/test/Microsoft.AspNet.Routing.Tests/Template/TemplateRouteTests.cs +++ b/test/Microsoft.AspNet.Routing.Tests/Template/TemplateRouteTests.cs @@ -423,7 +423,7 @@ namespace Microsoft.AspNet.Routing.Template.Tests // Assert Assert.Equal(2, constraints.Count); - Assert.IsType(constraints["controller"]); + Assert.IsType(constraints["controller"]); Assert.Equal(mockConstraint, constraints["action"]); } @@ -444,7 +444,7 @@ namespace Microsoft.AspNet.Routing.Template.Tests Assert.Equal(1, constraints.Count); var constraint = (CompositeRouteConstraint)constraints["id"]; Assert.IsType(constraint); - Assert.IsType(constraint.Constraints.ElementAt(0)); + Assert.IsType(constraint.Constraints.ElementAt(0)); Assert.IsType(constraint.Constraints.ElementAt(1)); } diff --git a/test/Microsoft.AspNet.Routing.Tests/TemplateParserDefaultValuesTests.cs b/test/Microsoft.AspNet.Routing.Tests/TemplateParserDefaultValuesTests.cs new file mode 100644 index 0000000000..feedcb40d9 --- /dev/null +++ b/test/Microsoft.AspNet.Routing.Tests/TemplateParserDefaultValuesTests.cs @@ -0,0 +1,117 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#if NET45 + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using Microsoft.AspNet.Http; +using Microsoft.AspNet.Routing.Constraints; +using Microsoft.Framework.DependencyInjection; +using Microsoft.Framework.DependencyInjection.Fallback; +using Microsoft.Framework.OptionsModel; +using Moq; +using Xunit; + +namespace Microsoft.AspNet.Routing.Tests +{ + public class TemplateParserDefaultValuesTests + { + private static IInlineConstraintResolver _inlineConstraintResolver = GetInlineConstraintResolver(); + + [Fact] + public void InlineDefaultValueSpecified_InlineValueIsUsed() + { + // Arrange & Act + var routeBuilder = CreateRouteBuilder(); + + // Act + routeBuilder.MapRoute("mockName", + "{controller}/{action}/{id:int=12}", + defaults: null, + constraints: null); + + // Assert + var defaults = ((Template.TemplateRoute)routeBuilder.Routes[0]).Defaults; + Assert.Equal("12", defaults["id"]); + } + + [Fact] + public void ExplicitDefaultValueSpecified_WithInlineDefaultValue_Throws() + { + // Arrange + var routeBuilder = CreateRouteBuilder(); + + // Act & Assert + var ex = Assert.Throws( + () => routeBuilder.MapRoute("mockName", + "{controller}/{action}/{id:int=12}", + defaults: new { id = 13 }, + constraints: null)); + + var message = "The route parameter 'id' has both an inline default value and an explicit default" + + " value specified. A route parameter cannot contain an inline default value when" + + " a default value is specified explicitly. Consider removing one of them."; + Assert.Equal(message, ex.Message); + } + + [Fact] + public void EmptyDefaultValue_WithOptionalParameter_Throws() + { + // Arrange + var routeBuilder = CreateRouteBuilder(); + + // Act & Assert + var ex = Assert.Throws( + () => routeBuilder.MapRoute("mockName", + "{controller}/{action}/{id:int=?}", + defaults: new { id = 13 }, + constraints: null)); + + var message = "An optional parameter cannot have default value.\r\nParameter name: routeTemplate"; + Assert.Equal(message, ex.Message); + } + + [Fact] + public void NonEmptyDefaultValue_WithOptionalParameter_Throws() + { + // Arrange + var routeBuilder = CreateRouteBuilder(); + + // Act & Assert + var ex = Assert.Throws( + () => routeBuilder.MapRoute("mockName", + "{controller}/{action}/{id:int=12?}", + defaults: new { id = 13 }, + constraints: null)); + + var message = "An optional parameter cannot have default value.\r\nParameter name: routeTemplate"; + Assert.Equal(message, ex.Message); + } + + private static IRouteBuilder CreateRouteBuilder() + { + var routeBuilder = new RouteBuilder(); + + routeBuilder.DefaultHandler = new Mock().Object; + + var serviceProviderMock = new Mock(); + serviceProviderMock.Setup(o => o.GetService(typeof(IInlineConstraintResolver))) + .Returns(_inlineConstraintResolver); + routeBuilder.ServiceProvider = serviceProviderMock.Object; + + return routeBuilder; + } + + private static IInlineConstraintResolver GetInlineConstraintResolver() + { + var services = new ServiceCollection { OptionsServices.GetDefaultServices() }; + var serviceProvider = services.BuildServiceProvider(); + var accessor = serviceProvider.GetService>(); + return new DefaultInlineConstraintResolver(serviceProvider, accessor); + } + } +} + +#endif \ No newline at end of file