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.
|
// 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 System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Abstractions;
|
using Microsoft.AspNet.Abstractions;
|
||||||
using Microsoft.AspNet.Routing;
|
using Microsoft.AspNet.Routing;
|
||||||
|
|
||||||
namespace RoutingSample
|
namespace RoutingSample
|
||||||
{
|
{
|
||||||
public class HttpContextRouteEndpoint : IRouteEndpoint
|
public class HttpContextRouteEndpoint : IRouter
|
||||||
{
|
{
|
||||||
private readonly RequestDelegate _appFunc;
|
private readonly RequestDelegate _appFunc;
|
||||||
|
|
||||||
|
|
@ -16,10 +15,16 @@ namespace RoutingSample
|
||||||
_appFunc = appFunc;
|
_appFunc = appFunc;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> Send(HttpContext context)
|
public async Task RouteAsync(RouteContext context)
|
||||||
{
|
{
|
||||||
await _appFunc(context);
|
await _appFunc(context.HttpContext);
|
||||||
return true;
|
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.
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Routing;
|
using Microsoft.AspNet.Routing;
|
||||||
|
|
||||||
namespace RoutingSample
|
namespace RoutingSample
|
||||||
{
|
{
|
||||||
internal class PrefixRoute : IRoute
|
internal class PrefixRoute : IRouter
|
||||||
{
|
{
|
||||||
private readonly IRouteEndpoint _endpoint;
|
private readonly IRouter _next;
|
||||||
private readonly string _prefix;
|
private readonly string _prefix;
|
||||||
|
|
||||||
public PrefixRoute(IRouteEndpoint endpoint, string prefix)
|
public PrefixRoute(IRouter next, string prefix)
|
||||||
{
|
{
|
||||||
_endpoint = endpoint;
|
_next = next;
|
||||||
|
|
||||||
if (prefix == null)
|
if (prefix == null)
|
||||||
{
|
{
|
||||||
|
|
@ -32,28 +33,26 @@ namespace RoutingSample
|
||||||
_prefix = prefix;
|
_prefix = prefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RouteMatch Match(RouteContext context)
|
public async Task RouteAsync(RouteContext context)
|
||||||
{
|
{
|
||||||
if (context.RequestPath.StartsWith(_prefix, StringComparison.OrdinalIgnoreCase))
|
if (context.RequestPath.StartsWith(_prefix, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
if (context.RequestPath.Length > _prefix.Length)
|
if (context.RequestPath.Length > _prefix.Length)
|
||||||
{
|
{
|
||||||
char next = context.RequestPath[_prefix.Length];
|
var lastCharacter = context.RequestPath[_prefix.Length];
|
||||||
if (next != '/' && next != '#' && next != '?')
|
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 endpoint1 = new HttpContextRouteEndpoint(async (context) => await context.Response.WriteAsync("match1"));
|
||||||
var endpoint2 = new HttpContextRouteEndpoint(async (context) => await context.Response.WriteAsync("Hello, World!"));
|
var endpoint2 = new HttpContextRouteEndpoint(async (context) => await context.Response.WriteAsync("Hello, World!"));
|
||||||
|
|
||||||
var rb1 = new RouteBuilder(endpoint1, routes.Routes);
|
routes.DefaultHandler = endpoint1;
|
||||||
rb1.AddPrefixRoute("api/store");
|
routes.AddPrefixRoute("api/store");
|
||||||
rb1.AddTemplateRoute("api/{controller}/{*extra}", new { controller = "Store" });
|
routes.AddTemplateRoute("api/{controller}/{*extra}", new { controller = "Store" });
|
||||||
|
|
||||||
var rb2 = new RouteBuilder(endpoint2, routes.Routes);
|
routes.AddPrefixRoute("hello/world", endpoint2);
|
||||||
rb2.AddPrefixRoute("hello/world");
|
routes.AddPrefixRoute("", endpoint2);
|
||||||
rb2.AddPrefixRoute("");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
namespace Microsoft.AspNet.Routing
|
||||||
{
|
{
|
||||||
public interface IRouteCollection
|
public interface IRouteCollection : IRouter
|
||||||
{
|
{
|
||||||
IRoute this[int index]
|
IRouter DefaultHandler { get; set; }
|
||||||
{
|
|
||||||
get;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Count
|
void Add(IRouter router);
|
||||||
{
|
|
||||||
get;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Add(IRoute route);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.
|
// 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
|
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 class BuilderExtensions
|
||||||
{
|
{
|
||||||
public static IRouteEngine UseRouter(this IBuilder builder)
|
public static IRouteCollection UseRouter(this IBuilder builder)
|
||||||
{
|
{
|
||||||
var routes = new DefaultRouteCollection();
|
return UseRouter(builder, new RouteCollection());
|
||||||
var engine = new DefaultRouteEngine(routes);
|
}
|
||||||
|
|
||||||
builder.Use((next) => new RouterMiddleware(next, engine).Invoke);
|
public static IRouteCollection UseRouter(this IBuilder builder, IRouteCollection routes)
|
||||||
|
{
|
||||||
return engine;
|
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.
|
// 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 System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Abstractions;
|
using Microsoft.AspNet.Abstractions;
|
||||||
|
|
||||||
|
|
@ -7,13 +8,13 @@ namespace Microsoft.AspNet.Routing.Owin
|
||||||
{
|
{
|
||||||
public class RouterMiddleware
|
public class RouterMiddleware
|
||||||
{
|
{
|
||||||
public RouterMiddleware(RequestDelegate next, IRouteEngine engine)
|
public RouterMiddleware(RequestDelegate next, IRouter route)
|
||||||
{
|
{
|
||||||
Next = next;
|
Next = next;
|
||||||
Engine = engine;
|
Route = route;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IRouteEngine Engine
|
private IRouter Route
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
set;
|
set;
|
||||||
|
|
@ -25,11 +26,14 @@ namespace Microsoft.AspNet.Routing.Owin
|
||||||
set;
|
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.
|
// 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;
|
using Microsoft.AspNet.Abstractions;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Routing
|
namespace Microsoft.AspNet.Routing
|
||||||
{
|
{
|
||||||
public class RouteContext
|
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
|
public HttpContext HttpContext { get; private set; }
|
||||||
{
|
|
||||||
get;
|
|
||||||
private set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string RequestPath
|
public bool IsHandled { get; set; }
|
||||||
{
|
|
||||||
get;
|
public string RequestPath { get; private set; }
|
||||||
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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Abstractions;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Routing.Template
|
namespace Microsoft.AspNet.Routing.Template
|
||||||
{
|
{
|
||||||
public class TemplateRoute : IRoute
|
public class TemplateRoute : IRouter
|
||||||
{
|
{
|
||||||
private readonly IDictionary<string, object> _defaults;
|
private readonly IDictionary<string, object> _defaults;
|
||||||
private readonly IRouteEndpoint _endpoint;
|
private readonly IRouter _next;
|
||||||
private readonly Template _parsedTemplate;
|
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;
|
||||||
|
|
||||||
public TemplateRoute(IRouteEndpoint endpoint, string routeTemplate)
|
public TemplateRoute(IRouter next, string routeTemplate)
|
||||||
: this(endpoint, routeTemplate, null)
|
: 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;
|
_routeTemplate = routeTemplate ?? string.Empty;
|
||||||
_defaults = defaults ?? new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
_defaults = defaults ?? new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
|
@ -42,17 +44,12 @@ namespace Microsoft.AspNet.Routing.Template
|
||||||
get { return _defaults; }
|
get { return _defaults; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public IRouteEndpoint Endpoint
|
|
||||||
{
|
|
||||||
get { return _endpoint; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public string RouteTemplate
|
public string RouteTemplate
|
||||||
{
|
{
|
||||||
get { return _routeTemplate; }
|
get { return _routeTemplate; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual RouteMatch Match(RouteContext context)
|
public async virtual Task RouteAsync(RouteContext context)
|
||||||
{
|
{
|
||||||
if (context == null)
|
if (context == null)
|
||||||
{
|
{
|
||||||
|
|
@ -69,18 +66,29 @@ namespace Microsoft.AspNet.Routing.Template
|
||||||
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
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
else
|
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);
|
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]
|
[Fact]
|
||||||
public void GetUrlWithEmptyStringForMiddleParameterShouldUseDefaultValue()
|
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();
|
var ambientValues = new RouteValueDictionary();
|
||||||
ambientValues.Add("Controller", "Test");
|
ambientValues.Add("Controller", "Test");
|
||||||
ambientValues.Add("Action", "Fallback");
|
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.
|
// PathString in HttpAbstractions guarantees a leading slash - so no value in testing other cases.
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Match_Success_LeadingSlash()
|
public async void Match_Success_LeadingSlash()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var route = CreateRoute("{controller}/{action}");
|
var route = CreateRoute("{controller}/{action}");
|
||||||
var context = CreateRouteContext("/Home/Index");
|
var context = CreateRouteContext("/Home/Index");
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var match = route.Match(context);
|
await route.RouteAsync(context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.NotNull(match);
|
Assert.True(context.IsHandled);
|
||||||
Assert.Equal(2, match.Values.Count);
|
Assert.Equal(2, context.Values.Count);
|
||||||
Assert.Equal("Home", match.Values["controller"]);
|
Assert.Equal("Home", context.Values["controller"]);
|
||||||
Assert.Equal("Index", match.Values["action"]);
|
Assert.Equal("Index", context.Values["action"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Match_Success_RootUrl()
|
public async void Match_Success_RootUrl()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var route = CreateRoute("");
|
var route = CreateRoute("");
|
||||||
var context = CreateRouteContext("/");
|
var context = CreateRouteContext("/");
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var match = route.Match(context);
|
await route.RouteAsync(context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.NotNull(match);
|
Assert.True(context.IsHandled);
|
||||||
Assert.Equal(0, match.Values.Count);
|
Assert.Equal(0, context.Values.Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Match_Success_Defaults()
|
public async void Match_Success_Defaults()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var route = CreateRoute("{controller}/{action}", new { action = "Index" });
|
var route = CreateRoute("{controller}/{action}", new { action = "Index" });
|
||||||
var context = CreateRouteContext("/Home");
|
var context = CreateRouteContext("/Home");
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var match = route.Match(context);
|
await route.RouteAsync(context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.NotNull(match);
|
Assert.True(context.IsHandled);
|
||||||
Assert.Equal(2, match.Values.Count);
|
Assert.Equal(2, context.Values.Count);
|
||||||
Assert.Equal("Home", match.Values["controller"]);
|
Assert.Equal("Home", context.Values["controller"]);
|
||||||
Assert.Equal("Index", match.Values["action"]);
|
Assert.Equal("Index", context.Values["action"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Match_Fails()
|
public async void Match_Fails()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var route = CreateRoute("{controller}/{action}");
|
var route = CreateRoute("{controller}/{action}");
|
||||||
var context = CreateRouteContext("/Home");
|
var context = CreateRouteContext("/Home");
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var match = route.Match(context);
|
await route.RouteAsync(context);
|
||||||
|
|
||||||
// Assert
|
// 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)
|
private static RouteContext CreateRouteContext(string requestPath)
|
||||||
|
|
@ -98,11 +113,11 @@ namespace Microsoft.AspNet.Routing.Template.Tests
|
||||||
var context = CreateRouteBindContext(new {controller = "Home"});
|
var context = CreateRouteBindContext(new {controller = "Home"});
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var bind = route.Bind(context);
|
route.BindPath(context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.NotNull(bind);
|
Assert.True(context.IsBound);
|
||||||
Assert.Equal("Home", bind.Url);
|
Assert.Equal("Home", context.Path);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -113,10 +128,26 @@ namespace Microsoft.AspNet.Routing.Template.Tests
|
||||||
var context = CreateRouteBindContext(new { controller = "Home" });
|
var context = CreateRouteBindContext(new { controller = "Home" });
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var bind = route.Bind(context);
|
route.BindPath(context);
|
||||||
|
|
||||||
// Assert
|
// 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]
|
[Fact]
|
||||||
|
|
@ -127,46 +158,54 @@ namespace Microsoft.AspNet.Routing.Template.Tests
|
||||||
var context = CreateRouteBindContext(new { action = "Index"}, new { controller = "Home" });
|
var context = CreateRouteBindContext(new { action = "Index"}, new { controller = "Home" });
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var bind = route.Bind(context);
|
route.BindPath(context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.NotNull(bind);
|
Assert.True(context.IsBound);
|
||||||
Assert.Equal("Home/Index", bind.Url);
|
Assert.Equal("Home/Index", context.Path);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static RouteBindContext CreateRouteBindContext(object values)
|
private static BindPathContext CreateRouteBindContext(object values)
|
||||||
{
|
{
|
||||||
return CreateRouteBindContext(new RouteValueDictionary(values), null);
|
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));
|
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);
|
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
|
#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