RouteConstraints Step II + III
Include Url Generation support Add unit tests Clean issues found by unit tests
This commit is contained in:
parent
2b87a625d9
commit
77ef7a5cde
|
|
@ -17,7 +17,7 @@ namespace Microsoft.AspNet.Routing
|
||||||
return BuildConstraintsCore(inputConstraints, routeTemplate);
|
return BuildConstraintsCore(inputConstraints, routeTemplate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IDictionary<string, IRouteConstraint>
|
private static IDictionary<string, IRouteConstraint>
|
||||||
BuildConstraintsCore(IDictionary<string, object> inputConstraints, string routeTemplate)
|
BuildConstraintsCore(IDictionary<string, object> inputConstraints, string routeTemplate)
|
||||||
{
|
{
|
||||||
if (inputConstraints == null || inputConstraints.Count == 0)
|
if (inputConstraints == null || inputConstraints.Count == 0)
|
||||||
|
|
@ -31,7 +31,7 @@ namespace Microsoft.AspNet.Routing
|
||||||
{
|
{
|
||||||
var constraint = kvp.Value as IRouteConstraint;
|
var constraint = kvp.Value as IRouteConstraint;
|
||||||
|
|
||||||
if (constraint == null)
|
if (constraint == null)
|
||||||
{
|
{
|
||||||
var regexPattern = kvp.Value as string;
|
var regexPattern = kvp.Value as string;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
|
||||||
using Microsoft.AspNet.Abstractions;
|
using Microsoft.AspNet.Abstractions;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Routing
|
namespace Microsoft.AspNet.Routing
|
||||||
{
|
{
|
||||||
public static class RouteConstraintMatcher
|
public static class RouteConstraintMatcher
|
||||||
{
|
{
|
||||||
public static bool Match([NotNull] IDictionary<string, IRouteConstraint> constraints,
|
public static bool Match(IDictionary<string, IRouteConstraint> constraints,
|
||||||
[NotNull] IDictionary<string, object> routeValues,
|
[NotNull] IDictionary<string, object> routeValues,
|
||||||
[NotNull] HttpContext httpContext,
|
[NotNull] HttpContext httpContext,
|
||||||
[NotNull] IRouter route,
|
[NotNull] IRouter route,
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ namespace Microsoft.AspNet.Routing.Template
|
||||||
private readonly IDictionary<string, object> _defaults;
|
private readonly IDictionary<string, object> _defaults;
|
||||||
private readonly IDictionary<string, IRouteConstraint> _constraints;
|
private readonly IDictionary<string, IRouteConstraint> _constraints;
|
||||||
private readonly IRouter _target;
|
private readonly IRouter _target;
|
||||||
private readonly Template _parsedTemplate;
|
|
||||||
private readonly string _routeTemplate;
|
private readonly string _routeTemplate;
|
||||||
private readonly TemplateMatcher _matcher;
|
private readonly TemplateMatcher _matcher;
|
||||||
private readonly TemplateBinder _binder;
|
private readonly TemplateBinder _binder;
|
||||||
|
|
@ -30,10 +29,10 @@ namespace Microsoft.AspNet.Routing.Template
|
||||||
_constraints = RouteConstraintBuilder.BuildConstraints(constraints, _routeTemplate);
|
_constraints = RouteConstraintBuilder.BuildConstraints(constraints, _routeTemplate);
|
||||||
|
|
||||||
// The parser will throw for invalid routes.
|
// The parser will throw for invalid routes.
|
||||||
_parsedTemplate = TemplateParser.Parse(RouteTemplate);
|
var parsedTemplate = TemplateParser.Parse(RouteTemplate);
|
||||||
|
|
||||||
_matcher = new TemplateMatcher(_parsedTemplate);
|
_matcher = new TemplateMatcher(parsedTemplate);
|
||||||
_binder = new TemplateBinder(_parsedTemplate, _defaults);
|
_binder = new TemplateBinder(parsedTemplate, _defaults);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IDictionary<string, object> Defaults
|
public IDictionary<string, object> Defaults
|
||||||
|
|
@ -46,6 +45,11 @@ namespace Microsoft.AspNet.Routing.Template
|
||||||
get { return _routeTemplate; }
|
get { return _routeTemplate; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IDictionary<string, IRouteConstraint> Constraints
|
||||||
|
{
|
||||||
|
get { return _constraints; }
|
||||||
|
}
|
||||||
|
|
||||||
public async virtual Task RouteAsync([NotNull] RouteContext context)
|
public async virtual Task RouteAsync([NotNull] RouteContext context)
|
||||||
{
|
{
|
||||||
var requestPath = context.RequestPath;
|
var requestPath = context.RequestPath;
|
||||||
|
|
@ -54,7 +58,7 @@ namespace Microsoft.AspNet.Routing.Template
|
||||||
requestPath = requestPath.Substring(1);
|
requestPath = requestPath.Substring(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
var values = _matcher.Match(requestPath, _defaults);
|
var values = _matcher.Match(requestPath, Defaults);
|
||||||
if (values == null)
|
if (values == null)
|
||||||
{
|
{
|
||||||
// If we got back a null value set, that means the URI did not match
|
// If we got back a null value set, that means the URI did not match
|
||||||
|
|
@ -65,7 +69,7 @@ namespace Microsoft.AspNet.Routing.Template
|
||||||
// Not currently doing anything to clean this up if it's not a match. Consider hardening this.
|
// Not currently doing anything to clean this up if it's not a match. Consider hardening this.
|
||||||
context.Values = values;
|
context.Values = values;
|
||||||
|
|
||||||
if (RouteConstraintMatcher.Match(_constraints,
|
if (RouteConstraintMatcher.Match(Constraints,
|
||||||
values,
|
values,
|
||||||
context.HttpContext,
|
context.HttpContext,
|
||||||
this,
|
this,
|
||||||
|
|
@ -85,6 +89,15 @@ namespace Microsoft.AspNet.Routing.Template
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!RouteConstraintMatcher.Match(Constraints,
|
||||||
|
values,
|
||||||
|
context.Context,
|
||||||
|
this,
|
||||||
|
RouteDirection.UrlGeneration))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// Validate that the target can accept these values.
|
// Validate that the target can accept these values.
|
||||||
var path = _target.GetVirtualPath(context);
|
var path = _target.GetVirtualPath(context);
|
||||||
if (path != null)
|
if (path != null)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
using Microsoft.AspNet.Abstractions;
|
||||||
|
using Moq;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Routing.Tests
|
||||||
|
{
|
||||||
|
public static class RouteConstraintExtensions
|
||||||
|
{
|
||||||
|
public static bool EasyMatch(this IRouteConstraint constraint,
|
||||||
|
string routeKey,
|
||||||
|
RouteValueDictionary values)
|
||||||
|
{
|
||||||
|
return constraint.Match(httpContext: new Mock<HttpContext>().Object,
|
||||||
|
route: new Mock<IRouter>().Object,
|
||||||
|
routeKey: routeKey,
|
||||||
|
values: values,
|
||||||
|
routeDirection: RouteDirection.IncomingRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,102 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.AspNet.Abstractions;
|
||||||
|
using Moq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Routing.Tests
|
||||||
|
{
|
||||||
|
public class ConstraintMatcherTests
|
||||||
|
{
|
||||||
|
private class PassConstraint : IRouteConstraint
|
||||||
|
{
|
||||||
|
public bool Match(HttpContext httpContext,
|
||||||
|
IRouter route,
|
||||||
|
string routeKey,
|
||||||
|
IDictionary<string, object> values,
|
||||||
|
RouteDirection routeDirection)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class FailConstraint : IRouteConstraint
|
||||||
|
{
|
||||||
|
public bool Match(HttpContext httpContext,
|
||||||
|
IRouter route,
|
||||||
|
string routeKey,
|
||||||
|
IDictionary<string, object> values,
|
||||||
|
RouteDirection routeDirection)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ReturnsTrueOnValidConstraints()
|
||||||
|
{
|
||||||
|
var constraints = new Dictionary<string, IRouteConstraint>
|
||||||
|
{
|
||||||
|
{"a", new PassConstraint()},
|
||||||
|
{"b", new PassConstraint()}
|
||||||
|
};
|
||||||
|
|
||||||
|
var routeValueDictionary = new RouteValueDictionary(new {a = "value", b = "value"});
|
||||||
|
|
||||||
|
Assert.True(RouteConstraintMatcher.Match(
|
||||||
|
constraints: constraints,
|
||||||
|
routeValues: routeValueDictionary,
|
||||||
|
httpContext: new Mock<HttpContext>().Object,
|
||||||
|
route: new Mock<IRouter>().Object,
|
||||||
|
routeDirection: RouteDirection.IncomingRequest));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ReturnsFalseOnInvalidConstraintsThatDontMatch()
|
||||||
|
{
|
||||||
|
var constraints = new Dictionary<string, IRouteConstraint>
|
||||||
|
{
|
||||||
|
{"a", new FailConstraint()},
|
||||||
|
{"b", new FailConstraint()}
|
||||||
|
};
|
||||||
|
|
||||||
|
var routeValueDictionary = new RouteValueDictionary(new { c = "value", d = "value" });
|
||||||
|
|
||||||
|
Assert.False(RouteConstraintMatcher.Match(
|
||||||
|
constraints: constraints,
|
||||||
|
routeValues: routeValueDictionary,
|
||||||
|
httpContext: new Mock<HttpContext>().Object,
|
||||||
|
route: new Mock<IRouter>().Object,
|
||||||
|
routeDirection: RouteDirection.IncomingRequest));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ReturnsFalseOnInvalidConstraintsThatMatch()
|
||||||
|
{
|
||||||
|
var constraints = new Dictionary<string, IRouteConstraint>
|
||||||
|
{
|
||||||
|
{"a", new FailConstraint()},
|
||||||
|
{"b", new FailConstraint()}
|
||||||
|
};
|
||||||
|
|
||||||
|
var routeValueDictionary = new RouteValueDictionary(new { a = "value", b = "value" });
|
||||||
|
|
||||||
|
Assert.False(RouteConstraintMatcher.Match(
|
||||||
|
constraints: constraints,
|
||||||
|
routeValues: routeValueDictionary,
|
||||||
|
httpContext: new Mock<HttpContext>().Object,
|
||||||
|
route: new Mock<IRouter>().Object,
|
||||||
|
routeDirection: RouteDirection.IncomingRequest));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ReturnsTrueOnNullInput()
|
||||||
|
{
|
||||||
|
Assert.True(RouteConstraintMatcher.Match(
|
||||||
|
constraints: null,
|
||||||
|
routeValues: new RouteValueDictionary(),
|
||||||
|
httpContext: new Mock<HttpContext>().Object,
|
||||||
|
route: new Mock<IRouter>().Object,
|
||||||
|
routeDirection: RouteDirection.IncomingRequest));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,132 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Abstractions;
|
||||||
|
using Microsoft.AspNet.Testing;
|
||||||
|
using Moq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Routing.Tests
|
||||||
|
{
|
||||||
|
public class ConstraintsBuilderTests
|
||||||
|
{
|
||||||
|
public static IEnumerable<object> EmptyAndNullDictionary
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return new[]
|
||||||
|
{
|
||||||
|
new Object[]
|
||||||
|
{
|
||||||
|
null,
|
||||||
|
},
|
||||||
|
|
||||||
|
new Object[]
|
||||||
|
{
|
||||||
|
new Dictionary<string, object>(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData("EmptyAndNullDictionary")]
|
||||||
|
public void ConstraintBuilderReturnsNull_OnNullOrEmptyInput(IDictionary<string, object> input)
|
||||||
|
{
|
||||||
|
var result = RouteConstraintBuilder.BuildConstraints(input);
|
||||||
|
|
||||||
|
Assert.Null(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData("EmptyAndNullDictionary")]
|
||||||
|
public void ConstraintBuilderWithTemplateReturnsNull_OnNullOrEmptyInput(IDictionary<string, object> input)
|
||||||
|
{
|
||||||
|
var result = RouteConstraintBuilder.BuildConstraints(input, "{controller}");
|
||||||
|
|
||||||
|
Assert.Null(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetRouteDataWithConstraintsThatIsAStringCreatesARegex()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var dictionary = new RouteValueDictionary(new { controller = "abc" });
|
||||||
|
var constraintDictionary = RouteConstraintBuilder.BuildConstraints(dictionary);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(1, constraintDictionary.Count);
|
||||||
|
Assert.Equal("controller", constraintDictionary.First().Key);
|
||||||
|
|
||||||
|
var constraint = constraintDictionary["controller"];
|
||||||
|
|
||||||
|
Assert.IsType<RegexConstraint>(constraint);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetRouteDataWithConstraintsThatIsCustomConstraint_IsPassThrough()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var originalConstraint = new Mock<IRouteConstraint>().Object;
|
||||||
|
|
||||||
|
var dictionary = new RouteValueDictionary(new { controller = originalConstraint });
|
||||||
|
var constraintDictionary = RouteConstraintBuilder.BuildConstraints(dictionary);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(1, constraintDictionary.Count);
|
||||||
|
Assert.Equal("controller", constraintDictionary.First().Key);
|
||||||
|
|
||||||
|
var constraint = constraintDictionary["controller"];
|
||||||
|
|
||||||
|
Assert.Equal(originalConstraint, constraint);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetRouteDataWithConstraintsThatIsNotStringOrCustomConstraint_Throws()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var dictionary = new RouteValueDictionary(new { controller = new RouteValueDictionary() });
|
||||||
|
|
||||||
|
ExceptionAssert.Throws<InvalidOperationException>(
|
||||||
|
() => RouteConstraintBuilder.BuildConstraints(dictionary),
|
||||||
|
"The constraint entry 'controller' must have a string value or be of a type which implements '" +
|
||||||
|
typeof(IRouteConstraint) + "'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RouteTemplateGetRouteDataWithConstraintsThatIsNotStringOrCustomConstraint_Throws()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var dictionary = new RouteValueDictionary(new { controller = new RouteValueDictionary() });
|
||||||
|
|
||||||
|
ExceptionAssert.Throws<InvalidOperationException>(
|
||||||
|
() => RouteConstraintBuilder.BuildConstraints(dictionary, "{controller}/{action}"),
|
||||||
|
"The constraint entry 'controller' on the route with route template " +
|
||||||
|
"'{controller}/{action}' must have a string value or be of a type which implements '" +
|
||||||
|
typeof(IRouteConstraint) + "'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("abc", "abc", true)]
|
||||||
|
[InlineData("Abc", "abc", true)]
|
||||||
|
[InlineData("Abc ", "abc", false)]
|
||||||
|
[InlineData("Abcd", "abc", false)]
|
||||||
|
[InlineData("Abc", " abc", false)]
|
||||||
|
public void StringConstraintsMatchesWholeValueCaseInsensitively(string routeValue,
|
||||||
|
string constraintValue,
|
||||||
|
bool shouldMatch)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var dictionary = new RouteValueDictionary(new { controller = routeValue });
|
||||||
|
|
||||||
|
var constraintDictionary = RouteConstraintBuilder.BuildConstraints(
|
||||||
|
new RouteValueDictionary(new { controller = constraintValue }));
|
||||||
|
var constraint = constraintDictionary["controller"];
|
||||||
|
|
||||||
|
Assert.Equal(shouldMatch,
|
||||||
|
constraint.EasyMatch("controller", dictionary));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Routing.Tests
|
||||||
|
{
|
||||||
|
public class RegexConstraintTests
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData("abc", "abc", true)]
|
||||||
|
[InlineData("Abc", "abc", true)]
|
||||||
|
[InlineData("Abc ", "abc", true)]
|
||||||
|
[InlineData("Abcd", "abc", true)]
|
||||||
|
[InlineData("^Abcd", "abc", true)]
|
||||||
|
[InlineData("Abc", " abc", false)]
|
||||||
|
public void RegexConstraintDoesNotPrepend(string routeValue,
|
||||||
|
string constraintValue,
|
||||||
|
bool shouldMatch)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var constraint = new RegexConstraint(constraintValue);
|
||||||
|
var values = new RouteValueDictionary(new {controller = routeValue});
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(shouldMatch, constraint.EasyMatch("controller", values));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RegexConstraintCanTakeARegex_SuccessulMatch()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var constraint = new RegexConstraint(new Regex("^abc$"));
|
||||||
|
var values = new RouteValueDictionary(new { controller = "abc"});
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(constraint.EasyMatch("controller", values));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RegexConstraintFailsIfKeyIsNotFound()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var constraint = new RegexConstraint(new Regex("^abc$"));
|
||||||
|
var values = new RouteValueDictionary(new { action = "abc" });
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(constraint.EasyMatch("controller", values));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RegexConstraintCanTakeARegex_FailedMatch()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var constraint = new RegexConstraint(new Regex("^abc$"));
|
||||||
|
var values = new RouteValueDictionary(new { controller = "Abc" });
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(constraint.EasyMatch("controller", values));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||||
|
|
||||||
#if NET45
|
#if NET45
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using Microsoft.AspNet.Testing;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Abstractions;
|
using Microsoft.AspNet.Abstractions;
|
||||||
using Moq;
|
using Moq;
|
||||||
|
|
@ -115,7 +116,7 @@ namespace Microsoft.AspNet.Routing.Template.Tests
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var route = CreateRoute("{controller}");
|
var route = CreateRoute("{controller}");
|
||||||
var context = CreateVirtualPathContext(new {controller = "Home"});
|
var context = CreateVirtualPathContext(new { controller = "Home" });
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var path = route.GetVirtualPath(context);
|
var path = route.GetVirtualPath(context);
|
||||||
|
|
@ -160,7 +161,7 @@ namespace Microsoft.AspNet.Routing.Template.Tests
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var route = CreateRoute("{controller}/{action}");
|
var route = CreateRoute("{controller}/{action}");
|
||||||
var context = CreateVirtualPathContext(new { action = "Index"}, new { controller = "Home" });
|
var context = CreateVirtualPathContext(new { action = "Index" }, new { controller = "Home" });
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var path = route.GetVirtualPath(context);
|
var path = route.GetVirtualPath(context);
|
||||||
|
|
@ -189,6 +190,48 @@ namespace Microsoft.AspNet.Routing.Template.Tests
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Route Registration
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RegisteringRouteWithInvalidConstraints_Throws()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var collection = new RouteCollection();
|
||||||
|
collection.DefaultHandler = new Mock<IRouter>().Object;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
ExceptionAssert.Throws<InvalidOperationException>(() => collection.MapRoute("{controller}/{action}",
|
||||||
|
defaults: null,
|
||||||
|
constraints: new { controller = "a.*", action = new Object() }),
|
||||||
|
"The constraint entry 'action' on the route with route template '{controller}/{action}' " +
|
||||||
|
"must have a string value or be of a type which implements '" +
|
||||||
|
typeof(IRouteConstraint) + "'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RegisteringRouteWithTwoConstraints()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var collection = new RouteCollection();
|
||||||
|
collection.DefaultHandler = new Mock<IRouter>().Object;
|
||||||
|
|
||||||
|
var mockConstraint = new Mock<IRouteConstraint>().Object;
|
||||||
|
|
||||||
|
collection.MapRoute("{controller}/{action}",
|
||||||
|
defaults: null,
|
||||||
|
constraints: new {controller = "a.*", action = mockConstraint});
|
||||||
|
|
||||||
|
var constraints = ((TemplateRoute) collection[0]).Constraints;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(2, constraints.Count);
|
||||||
|
Assert.IsType<RegexConstraint>(constraints["controller"]);
|
||||||
|
Assert.Equal(mockConstraint, constraints["action"]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
private static TemplateRoute CreateRoute(string template, bool accept = true)
|
private static TemplateRoute CreateRoute(string template, bool accept = true)
|
||||||
{
|
{
|
||||||
return new TemplateRoute(CreateTarget(accept), template);
|
return new TemplateRoute(CreateTarget(accept), template);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue