API changes for Routing

This commit is contained in:
Ryan Nowak 2014-03-05 16:48:10 -08:00
parent cd73fac433
commit 5d34a61bd9
27 changed files with 299 additions and 406 deletions

View File

@ -1,13 +1,12 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System;
using System.Threading.Tasks;
using Microsoft.AspNet.Abstractions;
using Microsoft.AspNet.Routing;
namespace RoutingSample
{
public class HttpContextRouteEndpoint : IRouteEndpoint
public class HttpContextRouteEndpoint : IRouter
{
private readonly RequestDelegate _appFunc;
@ -16,10 +15,16 @@ namespace RoutingSample
_appFunc = appFunc;
}
public async Task<bool> Send(HttpContext context)
public async Task RouteAsync(RouteContext context)
{
await _appFunc(context);
return true;
await _appFunc(context.HttpContext);
context.IsHandled = true;
}
public void BindPath(BindPathContext context)
{
// We can generate a url for anything that the parent route deems OK.
context.IsBound = true;
}
}
}

View File

@ -1,18 +1,19 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System;
using System.Threading.Tasks;
using Microsoft.AspNet.Routing;
namespace RoutingSample
{
internal class PrefixRoute : IRoute
internal class PrefixRoute : IRouter
{
private readonly IRouteEndpoint _endpoint;
private readonly IRouter _next;
private readonly string _prefix;
public PrefixRoute(IRouteEndpoint endpoint, string prefix)
public PrefixRoute(IRouter next, string prefix)
{
_endpoint = endpoint;
_next = next;
if (prefix == null)
{
@ -32,28 +33,26 @@ namespace RoutingSample
_prefix = prefix;
}
public RouteMatch Match(RouteContext context)
public async Task RouteAsync(RouteContext context)
{
if (context.RequestPath.StartsWith(_prefix, StringComparison.OrdinalIgnoreCase))
{
if (context.RequestPath.Length > _prefix.Length)
{
char next = context.RequestPath[_prefix.Length];
if (next != '/' && next != '#' && next != '?')
var lastCharacter = context.RequestPath[_prefix.Length];
if (lastCharacter != '/' && lastCharacter != '#' && lastCharacter != '?')
{
return null;
return;
}
}
return new RouteMatch(_endpoint);
await _next.RouteAsync(context);
}
return null;
}
public RouteBindResult Bind(RouteBindContext context)
public void BindPath(BindPathContext context)
{
return null;
// Do nothing
}
}
}

View File

@ -1,14 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using Microsoft.AspNet.Routing;
namespace RoutingSample
{
public static class RouteBuilderExtensions
{
public static void AddPrefixRoute(this IRouteBuilder builder, string prefix)
{
builder.Routes.Add(new PrefixRoute(builder.Endpoint, prefix));
}
}
}

View File

@ -0,0 +1,20 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using Microsoft.AspNet.Routing;
namespace RoutingSample
{
public static class RouteCollectionExtensions
{
public static IRouteCollection AddPrefixRoute(this IRouteCollection routes, string prefix)
{
return AddPrefixRoute(routes, prefix, routes.DefaultHandler);
}
public static IRouteCollection AddPrefixRoute(this IRouteCollection routes, string prefix, IRouter handler)
{
routes.Add(new PrefixRoute(handler, prefix));
return routes;
}
}
}

View File

@ -26,13 +26,12 @@ namespace RoutingSample
var endpoint1 = new HttpContextRouteEndpoint(async (context) => await context.Response.WriteAsync("match1"));
var endpoint2 = new HttpContextRouteEndpoint(async (context) => await context.Response.WriteAsync("Hello, World!"));
var rb1 = new RouteBuilder(endpoint1, routes.Routes);
rb1.AddPrefixRoute("api/store");
rb1.AddTemplateRoute("api/{controller}/{*extra}", new { controller = "Store" });
routes.DefaultHandler = endpoint1;
routes.AddPrefixRoute("api/store");
routes.AddTemplateRoute("api/{controller}/{*extra}", new { controller = "Store" });
var rb2 = new RouteBuilder(endpoint2, routes.Routes);
rb2.AddPrefixRoute("hello/world");
rb2.AddPrefixRoute("");
routes.AddPrefixRoute("hello/world", endpoint2);
routes.AddPrefixRoute("", endpoint2);
}
}
}

View File

@ -0,0 +1,27 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using Microsoft.AspNet.Abstractions;
namespace Microsoft.AspNet.Routing
{
public class BindPathContext
{
public BindPathContext(HttpContext context, IDictionary<string, object> ambientValues, IDictionary<string, object> values)
{
Context = context;
AmbientValues = ambientValues;
Values = values;
}
public IDictionary<string, object> AmbientValues { get; private set; }
public HttpContext Context { get; private set; }
public bool IsBound { get; set; }
public string Path { get; set; }
public IDictionary<string, object> Values { get; private set; }
}
}

View File

@ -1,26 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
namespace Microsoft.AspNet.Routing
{
public class DefaultRouteCollection : IRouteCollection
{
private readonly List<IRoute> _routes = new List<IRoute>();
public IRoute this[int index]
{
get { return _routes[index]; }
}
public int Count
{
get { return _routes.Count; }
}
public void Add(IRoute route)
{
_routes.Add(route);
}
}
}

View File

@ -1,64 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNet.Abstractions;
namespace Microsoft.AspNet.Routing
{
public class DefaultRouteEngine : IRouteEngine
{
public DefaultRouteEngine(IRouteCollection routes)
{
Routes = routes;
}
public IRouteCollection Routes
{
get;
private set;
}
public async Task<bool> Invoke(HttpContext context)
{
var routeContext = new RouteContext(context);
for (var i = 0; i < Routes.Count; i++)
{
var route = Routes[i];
var match = route.Match(routeContext);
if (match != null)
{
context.SetFeature<IRouteValues>(new RouteValues(match.Values));
var accepted = await match.Endpoint.Send(context);
if (accepted)
{
return true;
}
}
}
return false;
}
public string GetUrl(HttpContext context, IDictionary<string, object> values)
{
var routeBindContext = new RouteBindContext(context, values);
for (var i = 0; i < Routes.Count; i++)
{
var route = Routes[i];
var result = route.Bind(routeBindContext);
if (result != null)
{
return result.Url;
}
}
return null;
}
}
}

View File

@ -1,17 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
namespace Microsoft.AspNet.Routing
{
public interface IRouteBuilder
{
IRouteEndpoint Endpoint
{
get;
}
IRouteCollection Routes
{
get;
}
}
}

View File

@ -1,19 +1,11 @@
// 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.
namespace Microsoft.AspNet.Routing
{
public interface IRouteCollection
public interface IRouteCollection : IRouter
{
IRoute this[int index]
{
get;
}
IRouter DefaultHandler { get; set; }
int Count
{
get;
}
void Add(IRoute route);
void Add(IRouter router);
}
}

View File

@ -1,12 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Threading.Tasks;
using Microsoft.AspNet.Abstractions;
namespace Microsoft.AspNet.Routing
{
public interface IRouteEndpoint
{
Task<bool> Send(HttpContext context);
}
}

View File

@ -1,17 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNet.Abstractions;
namespace Microsoft.AspNet.Routing
{
public interface IRouteEngine
{
IRouteCollection Routes { get; }
Task<bool> Invoke(HttpContext context);
string GetUrl(HttpContext context, IDictionary<string, object> values);
}
}

View File

@ -1,11 +1,13 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Threading.Tasks;
namespace Microsoft.AspNet.Routing
{
public interface IRoute
public interface IRouter
{
RouteMatch Match(RouteContext context);
Task RouteAsync(RouteContext context);
RouteBindResult Bind(RouteBindContext context);
void BindPath(BindPathContext context);
}
}

View File

@ -6,14 +6,15 @@ namespace Microsoft.AspNet.Routing.Owin
{
public static class BuilderExtensions
{
public static IRouteEngine UseRouter(this IBuilder builder)
public static IRouteCollection UseRouter(this IBuilder builder)
{
var routes = new DefaultRouteCollection();
var engine = new DefaultRouteEngine(routes);
return UseRouter(builder, new RouteCollection());
}
builder.Use((next) => new RouterMiddleware(next, engine).Invoke);
return engine;
public static IRouteCollection UseRouter(this IBuilder builder, IRouteCollection routes)
{
builder.Use((next) => new RouterMiddleware(next, routes).Invoke);
return routes;
}
}
}

View File

@ -1,5 +1,6 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNet.Abstractions;
@ -7,13 +8,13 @@ namespace Microsoft.AspNet.Routing.Owin
{
public class RouterMiddleware
{
public RouterMiddleware(RequestDelegate next, IRouteEngine engine)
public RouterMiddleware(RequestDelegate next, IRouter route)
{
Next = next;
Engine = engine;
Route = route;
}
private IRouteEngine Engine
private IRouter Route
{
get;
set;
@ -25,11 +26,14 @@ namespace Microsoft.AspNet.Routing.Owin
set;
}
public async Task Invoke(HttpContext context)
public async Task Invoke(HttpContext httpContext)
{
if (!(await Engine.Invoke(context)))
var context = new RouteContext(httpContext);
await Route.RouteAsync(context);
if (!context.IsHandled)
{
await Next.Invoke(context);
await Next.Invoke(httpContext);
}
}
}

View File

@ -1,28 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using Microsoft.AspNet.Abstractions;
namespace Microsoft.AspNet.Routing
{
public class RouteBindContext
{
public RouteBindContext(HttpContext context, IDictionary<string, object> values)
{
Context = context;
Values = values;
if (Context != null)
{
var ambientValues = context.GetFeature<IRouteValues>();
AmbientValues = ambientValues == null ? null : ambientValues.Values;
}
}
public IDictionary<string, object> AmbientValues { get; private set; }
public HttpContext Context { get; private set; }
public IDictionary<string, object> Values { get; private set; }
}
}

View File

@ -1,14 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
namespace Microsoft.AspNet.Routing
{
public class RouteBindResult
{
public RouteBindResult(string url)
{
Url = url;
}
public string Url { get; private set; }
}
}

View File

@ -1,24 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
namespace Microsoft.AspNet.Routing
{
public class RouteBuilder : IRouteBuilder
{
public RouteBuilder(IRouteEndpoint endpoint, IRouteCollection routes)
{
Endpoint = endpoint;
Routes = routes;
}
public IRouteEndpoint Endpoint
{
get;
private set;
}
public IRouteCollection Routes
{
get;
private set;
}
}
}

View File

@ -0,0 +1,58 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNet.Abstractions;
namespace Microsoft.AspNet.Routing
{
public class RouteCollection : IRouteCollection
{
private readonly List<IRouter> _routes = new List<IRouter>();
public IRouter this[int index]
{
get { return _routes[index]; }
}
public int Count
{
get { return _routes.Count; }
}
public IRouter DefaultHandler { get; set; }
public void Add(IRouter router)
{
_routes.Add(router);
}
public async virtual Task RouteAsync(RouteContext context)
{
for (var i = 0; i < Count; i++)
{
var route = this[i];
await route.RouteAsync(context);
if (context.IsHandled)
{
return;
}
}
}
public virtual void BindPath(BindPathContext context)
{
for (var i = 0; i < Count; i++)
{
var route = this[i];
route.BindPath(context);
if (context.IsBound)
{
return;
}
}
}
}
}

View File

@ -1,28 +1,25 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using Microsoft.AspNet.Abstractions;
namespace Microsoft.AspNet.Routing
{
public class RouteContext
{
public RouteContext(HttpContext context)
public RouteContext(HttpContext httpContext)
{
Context = context;
HttpContext = httpContext;
RequestPath = context.Request.Path.Value;
RequestPath = httpContext.Request.Path.Value;
}
public HttpContext Context
{
get;
private set;
}
public HttpContext HttpContext { get; private set; }
public string RequestPath
{
get;
private set;
}
public bool IsHandled { get; set; }
public string RequestPath { get; private set; }
public IDictionary<string, object> Values { get; set; }
}
}

View File

@ -1,36 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
namespace Microsoft.AspNet.Routing
{
/// <summary>
/// The result of matching a route. Includes an <see cref="IRouteEndpoint"/> to invoke and an optional collection of
/// captured values.
/// </summary>
public class RouteMatch
{
public RouteMatch(IRouteEndpoint endpoint)
: this(endpoint, null)
{
}
public RouteMatch(IRouteEndpoint endpoint, IDictionary<string, object> values)
{
Endpoint = endpoint;
Values = values;
}
public IRouteEndpoint Endpoint
{
get;
private set;
}
public IDictionary<string, object> Values
{
get;
private set;
}
}
}

View File

@ -1,9 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
namespace Microsoft.AspNet.Routing.Template
{
public class BoundRouteTemplate
{
public string Path { get; set; }
}
}

View File

@ -1,24 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
namespace Microsoft.AspNet.Routing.Template
{
public static class RouteBuilderExtensions
{
public static void AddTemplateRoute(this IRouteBuilder builder, string template)
{
AddTemplateRoute(builder, template, null);
}
public static void AddTemplateRoute(this IRouteBuilder builder, string template, IDictionary<string, object> defaults)
{
builder.Routes.Add(new TemplateRoute(builder.Endpoint, template, defaults));
}
public static void AddTemplateRoute(this IRouteBuilder builder, string template, object defaults)
{
builder.Routes.Add(new TemplateRoute(builder.Endpoint, template, new RouteValueDictionary(defaults)));
}
}
}

View File

@ -0,0 +1,27 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
namespace Microsoft.AspNet.Routing.Template
{
public static class RouteCollectionExtensions
{
public static IRouteCollection AddTemplateRoute(this IRouteCollection routes, string template)
{
AddTemplateRoute(routes, template, null);
return routes;
}
public static IRouteCollection AddTemplateRoute(this IRouteCollection routes, string template, object defaults)
{
AddTemplateRoute(routes, template, new RouteValueDictionary(defaults));
return routes;
}
public static IRouteCollection AddTemplateRoute(this IRouteCollection routes, string template, IDictionary<string, object> defaults)
{
routes.Add(new TemplateRoute(routes.DefaultHandler, template, defaults));
return routes;
}
}
}

View File

@ -2,31 +2,33 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNet.Abstractions;
namespace Microsoft.AspNet.Routing.Template
{
public class TemplateRoute : IRoute
public class TemplateRoute : IRouter
{
private readonly IDictionary<string, object> _defaults;
private readonly IRouteEndpoint _endpoint;
private readonly IRouter _next;
private readonly Template _parsedTemplate;
private readonly string _routeTemplate;
private readonly TemplateMatcher _matcher;
private readonly TemplateBinder _binder;
public TemplateRoute(IRouteEndpoint endpoint, string routeTemplate)
: this(endpoint, routeTemplate, null)
public TemplateRoute(IRouter next, string routeTemplate)
: this(next, routeTemplate, null)
{
}
public TemplateRoute(IRouteEndpoint endpoint, string routeTemplate, IDictionary<string, object> defaults)
public TemplateRoute(IRouter next, string routeTemplate, IDictionary<string, object> defaults)
{
if (endpoint == null)
if (next == null)
{
throw new ArgumentNullException("endpoint");
throw new ArgumentNullException("next");
}
_endpoint = endpoint;
_next = next;
_routeTemplate = routeTemplate ?? string.Empty;
_defaults = defaults ?? new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
@ -42,17 +44,12 @@ namespace Microsoft.AspNet.Routing.Template
get { return _defaults; }
}
public IRouteEndpoint Endpoint
{
get { return _endpoint; }
}
public string RouteTemplate
{
get { return _routeTemplate; }
}
public virtual RouteMatch Match(RouteContext context)
public async virtual Task RouteAsync(RouteContext context)
{
if (context == null)
{
@ -69,18 +66,29 @@ namespace Microsoft.AspNet.Routing.Template
if (values == null)
{
// If we got back a null value set, that means the URI did not match
return null;
return;
}
else
{
return new RouteMatch(_endpoint, values);
await _next.RouteAsync(new RouteContext(context.HttpContext){ Values = values });
}
}
public RouteBindResult Bind(RouteBindContext context)
public void BindPath(BindPathContext context)
{
// This could be optimized more heavily - right now we try to do the full url
// generation before validating, but we could do it in two phases.
var path = _binder.Bind(_defaults, context.AmbientValues, context.Values);
return path == null ? null : new RouteBindResult(path);
if (path == null)
{
return;
}
_next.BindPath(context);
if (context.IsBound && context.Path == null)
{
context.Path = path;
}
}
}
}

View File

@ -439,7 +439,7 @@ namespace Microsoft.AspNet.Routing.Template.Tests
[Fact]
public void GetUrlWithEmptyStringForMiddleParameterShouldUseDefaultValue()
{
// DevDiv Bugs 172084: UrlRouting: Route.GetUrl generates the wrong route of new values has a different controller and route has an action parameter with default
// DevDiv Bugs 172084: UrlRouting: Route.BindPath generates the wrong route of new values has a different controller and route has an action parameter with default
var ambientValues = new RouteValueDictionary();
ambientValues.Add("Controller", "Test");
ambientValues.Add("Action", "Fallback");

View File

@ -13,66 +13,81 @@ namespace Microsoft.AspNet.Routing.Template.Tests
// PathString in HttpAbstractions guarantees a leading slash - so no value in testing other cases.
[Fact]
public void Match_Success_LeadingSlash()
public async void Match_Success_LeadingSlash()
{
// Arrange
var route = CreateRoute("{controller}/{action}");
var context = CreateRouteContext("/Home/Index");
// Act
var match = route.Match(context);
await route.RouteAsync(context);
// Assert
Assert.NotNull(match);
Assert.Equal(2, match.Values.Count);
Assert.Equal("Home", match.Values["controller"]);
Assert.Equal("Index", match.Values["action"]);
Assert.True(context.IsHandled);
Assert.Equal(2, context.Values.Count);
Assert.Equal("Home", context.Values["controller"]);
Assert.Equal("Index", context.Values["action"]);
}
[Fact]
public void Match_Success_RootUrl()
public async void Match_Success_RootUrl()
{
// Arrange
var route = CreateRoute("");
var context = CreateRouteContext("/");
// Act
var match = route.Match(context);
await route.RouteAsync(context);
// Assert
Assert.NotNull(match);
Assert.Equal(0, match.Values.Count);
Assert.True(context.IsHandled);
Assert.Equal(0, context.Values.Count);
}
[Fact]
public void Match_Success_Defaults()
public async void Match_Success_Defaults()
{
// Arrange
var route = CreateRoute("{controller}/{action}", new { action = "Index" });
var context = CreateRouteContext("/Home");
// Act
var match = route.Match(context);
await route.RouteAsync(context);
// Assert
Assert.NotNull(match);
Assert.Equal(2, match.Values.Count);
Assert.Equal("Home", match.Values["controller"]);
Assert.Equal("Index", match.Values["action"]);
Assert.True(context.IsHandled);
Assert.Equal(2, context.Values.Count);
Assert.Equal("Home", context.Values["controller"]);
Assert.Equal("Index", context.Values["action"]);
}
[Fact]
public void Match_Fails()
public async void Match_Fails()
{
// Arrange
var route = CreateRoute("{controller}/{action}");
var context = CreateRouteContext("/Home");
// Act
var match = route.Match(context);
await route.RouteAsync(context);
// Assert
Assert.Null(match);
Assert.False(context.IsHandled);
}
[Fact]
public async void Match_RejectedByHanlder()
{
// Arrange
var route = CreateRoute("{controller}", accept: false);
var context = CreateRouteContext("/Home");
// Act
await route.RouteAsync(context);
// Assert
Assert.False(context.IsHandled);
Assert.Null(context.Values);
}
private static RouteContext CreateRouteContext(string requestPath)
@ -98,11 +113,11 @@ namespace Microsoft.AspNet.Routing.Template.Tests
var context = CreateRouteBindContext(new {controller = "Home"});
// Act
var bind = route.Bind(context);
route.BindPath(context);
// Assert
Assert.NotNull(bind);
Assert.Equal("Home", bind.Url);
Assert.True(context.IsBound);
Assert.Equal("Home", context.Path);
}
[Fact]
@ -113,10 +128,26 @@ namespace Microsoft.AspNet.Routing.Template.Tests
var context = CreateRouteBindContext(new { controller = "Home" });
// Act
var bind = route.Bind(context);
route.BindPath(context);
// Assert
Assert.Null(bind);
Assert.False(context.IsBound);
Assert.Null(context.Path);
}
[Fact]
public void Bind_RejectedByHandler()
{
// Arrange
var route = CreateRoute("{controller}", accept: false);
var context = CreateRouteBindContext(new { controller = "Home" });
// Act
route.BindPath(context);
// Assert
Assert.False(context.IsBound);
Assert.Null(context.Path);
}
[Fact]
@ -127,46 +158,54 @@ namespace Microsoft.AspNet.Routing.Template.Tests
var context = CreateRouteBindContext(new { action = "Index"}, new { controller = "Home" });
// Act
var bind = route.Bind(context);
route.BindPath(context);
// Assert
Assert.NotNull(bind);
Assert.Equal("Home/Index", bind.Url);
Assert.True(context.IsBound);
Assert.Equal("Home/Index", context.Path);
}
private static RouteBindContext CreateRouteBindContext(object values)
private static BindPathContext CreateRouteBindContext(object values)
{
return CreateRouteBindContext(new RouteValueDictionary(values), null);
}
private static RouteBindContext CreateRouteBindContext(object values, object ambientValues)
private static BindPathContext CreateRouteBindContext(object values, object ambientValues)
{
return CreateRouteBindContext(new RouteValueDictionary(values), new RouteValueDictionary(ambientValues));
}
private static RouteBindContext CreateRouteBindContext(IDictionary<string, object> values, IDictionary<string, object> ambientValues)
private static BindPathContext CreateRouteBindContext(IDictionary<string, object> values, IDictionary<string, object> ambientValues)
{
var context = new Mock<HttpContext>(MockBehavior.Strict);
context.Setup(c => c.GetFeature<IRouteValues>()).Returns(new RouteValues(ambientValues));
return new RouteBindContext(context.Object, values);
return new BindPathContext(context.Object, ambientValues, values);
}
#endregion
private static TemplateRoute CreateRoute(string template)
private static TemplateRoute CreateRoute(string template, bool accept = true)
{
return new TemplateRoute(CreateEndpoint(), template);
return new TemplateRoute(CreateEndpoint(accept), template);
}
private static TemplateRoute CreateRoute(string template, object defaults)
private static TemplateRoute CreateRoute(string template, object defaults, bool accept = true)
{
return new TemplateRoute(CreateEndpoint(), template, new RouteValueDictionary(defaults));
return new TemplateRoute(CreateEndpoint(accept), template, new RouteValueDictionary(defaults));
}
private static IRouteEndpoint CreateEndpoint()
private static IRouter CreateEndpoint(bool accept = true)
{
return new Mock<IRouteEndpoint>(MockBehavior.Strict).Object;
var endpoint = new Mock<IRouter>(MockBehavior.Strict);
endpoint
.Setup(e => e.BindPath(It.IsAny<BindPathContext>()))
.Callback<BindPathContext>(c => c.IsBound = accept);
endpoint
.Setup(e => e.RouteAsync(It.IsAny<RouteContext>()))
.Callback<RouteContext>(async (c) => c.IsHandled = accept);
return endpoint.Object;
}
}
}