API changes for Routing
This commit is contained in:
parent
cd73fac433
commit
5d34a61bd9
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue