Constraints
This commit is contained in:
parent
cf16d6cba7
commit
a917cbd577
|
|
@ -1,21 +1,22 @@
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Abstractions;
|
|
||||||
using Microsoft.AspNet.Routing;
|
using Microsoft.AspNet.Routing;
|
||||||
|
|
||||||
namespace RoutingSample.Web
|
namespace RoutingSample.Web
|
||||||
{
|
{
|
||||||
public class HttpContextRouteEndpoint : IRouter
|
public class DelegateRouteEndpoint : IRouter
|
||||||
{
|
{
|
||||||
private readonly RequestDelegate _appFunc;
|
public delegate Task RoutedDelegate(RouteContext context);
|
||||||
|
|
||||||
public HttpContextRouteEndpoint(RequestDelegate appFunc)
|
private readonly RoutedDelegate _appFunc;
|
||||||
|
|
||||||
|
public DelegateRouteEndpoint(RoutedDelegate appFunc)
|
||||||
{
|
{
|
||||||
_appFunc = appFunc;
|
_appFunc = appFunc;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RouteAsync(RouteContext context)
|
public async Task RouteAsync(RouteContext context)
|
||||||
{
|
{
|
||||||
await _appFunc(context.HttpContext);
|
await _appFunc(context);
|
||||||
context.IsHandled = true;
|
context.IsHandled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace RoutingSample.Web
|
||||||
|
{
|
||||||
|
public static class DictionaryExtensions
|
||||||
|
{
|
||||||
|
public static string Print(this IDictionary<string, object> routeValues)
|
||||||
|
{
|
||||||
|
var values = routeValues.Select(kvp => kvp.Key + ":" + kvp.Value.ToString());
|
||||||
|
|
||||||
|
return string.Join(" ", values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using Microsoft.AspNet.Abstractions;
|
using System.Text.RegularExpressions;
|
||||||
|
using Microsoft.AspNet.Abstractions;
|
||||||
using Microsoft.AspNet.Routing;
|
using Microsoft.AspNet.Routing;
|
||||||
|
|
||||||
namespace RoutingSample.Web
|
namespace RoutingSample.Web
|
||||||
|
|
@ -9,11 +10,23 @@ namespace RoutingSample.Web
|
||||||
{
|
{
|
||||||
var routes = builder.UseRouter();
|
var routes = builder.UseRouter();
|
||||||
|
|
||||||
var endpoint1 = new HttpContextRouteEndpoint(async (context) => await context.Response.WriteAsync("match1"));
|
var endpoint1 = new DelegateRouteEndpoint(async (context) =>
|
||||||
var endpoint2 = new HttpContextRouteEndpoint(async (context) => await context.Response.WriteAsync("Hello, World!"));
|
await context.HttpContext.Response.WriteAsync(
|
||||||
|
"match1, route values -" + context.Values.Print()));
|
||||||
|
var endpoint2 = new DelegateRouteEndpoint(async (context) =>
|
||||||
|
await context.HttpContext.Response.WriteAsync("Hello, World!"));
|
||||||
|
|
||||||
routes.DefaultHandler = endpoint1;
|
routes.DefaultHandler = endpoint1;
|
||||||
routes.AddPrefixRoute("api/store");
|
routes.AddPrefixRoute("api/store");
|
||||||
|
|
||||||
|
routes.MapRoute("api/constraint/{controller}", null, new { controller = "my.*" });
|
||||||
|
routes.MapRoute("api/rconstraint/{controller}",
|
||||||
|
new { foo = "Bar" },
|
||||||
|
new { controller = new RegexConstraint("^(my.*)$") });
|
||||||
|
routes.MapRoute("api/r2constraint/{controller}",
|
||||||
|
new { foo = "Bar2" },
|
||||||
|
new { controller = new RegexConstraint(new Regex("^(my.*)$")) });
|
||||||
|
|
||||||
routes.MapRoute("api/{controller}/{*extra}", new { controller = "Store" });
|
routes.MapRoute("api/{controller}/{*extra}", new { controller = "Store" });
|
||||||
|
|
||||||
routes.AddPrefixRoute("hello/world", endpoint2);
|
routes.AddPrefixRoute("hello/world", endpoint2);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.AspNet.Abstractions;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Routing
|
||||||
|
{
|
||||||
|
public interface IRouteConstraint
|
||||||
|
{
|
||||||
|
bool Match([NotNull] HttpContext httpContext,
|
||||||
|
[NotNull] IRouter route,
|
||||||
|
[NotNull] string routeKey,
|
||||||
|
[NotNull] IDictionary<string, object> values,
|
||||||
|
RouteDirection routeDirection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,14 +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 interface IRouteValues
|
|
||||||
{
|
|
||||||
IDictionary<string, object> Values
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Routing
|
||||||
|
{
|
||||||
|
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
|
||||||
|
internal sealed class NotNullAttribute : Attribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Microsoft.AspNet.Abstractions;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Routing
|
||||||
|
{
|
||||||
|
public class RegexConstraint : IRouteConstraint
|
||||||
|
{
|
||||||
|
public RegexConstraint([NotNull] Regex regex)
|
||||||
|
{
|
||||||
|
Constraint = regex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RegexConstraint([NotNull] string regexPattern)
|
||||||
|
{
|
||||||
|
Constraint = new Regex(regexPattern, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Regex Constraint { get; private set; }
|
||||||
|
|
||||||
|
public bool Match([NotNull] HttpContext httpContext,
|
||||||
|
[NotNull] IRouter route,
|
||||||
|
[NotNull] string routeKey,
|
||||||
|
[NotNull] IDictionary<string, object> routeValues,
|
||||||
|
RouteDirection routeDirection)
|
||||||
|
{
|
||||||
|
object routeValue;
|
||||||
|
|
||||||
|
if (routeValues.TryGetValue(routeKey, out routeValue)
|
||||||
|
&& routeValue != null)
|
||||||
|
{
|
||||||
|
var parameterValueString = Convert.ToString(routeValue, CultureInfo.InvariantCulture);
|
||||||
|
|
||||||
|
return Constraint.IsMatch(parameterValueString);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
// 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 System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,39 @@ namespace Microsoft.AspNet.Routing
|
||||||
throw new ArgumentException("DefaultHandler must be set.");
|
throw new ArgumentException("DefaultHandler must be set.");
|
||||||
}
|
}
|
||||||
|
|
||||||
routes.Add(new TemplateRoute(routes.DefaultHandler, template, defaults));
|
routes.Add(new TemplateRoute(routes.DefaultHandler, template, defaults, null));
|
||||||
|
return routes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IRouteCollection MapRoute(this IRouteCollection routes, string template, object defaults, object constraints)
|
||||||
|
{
|
||||||
|
MapRoute(routes, template, new RouteValueDictionary(defaults), new RouteValueDictionary(constraints));
|
||||||
|
return routes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IRouteCollection MapRoute(this IRouteCollection routes, string template, object defaults,
|
||||||
|
IDictionary<string, object> constraints)
|
||||||
|
{
|
||||||
|
MapRoute(routes, template, new RouteValueDictionary(defaults), constraints);
|
||||||
|
return routes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IRouteCollection MapRoute(this IRouteCollection routes, string template,
|
||||||
|
IDictionary<string, object> defaults, object constraints)
|
||||||
|
{
|
||||||
|
MapRoute(routes, template, defaults, new RouteValueDictionary(constraints));
|
||||||
|
return routes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IRouteCollection MapRoute(this IRouteCollection routes, string template,
|
||||||
|
IDictionary<string, object> defaults, IDictionary<string, object> constraints)
|
||||||
|
{
|
||||||
|
if (routes.DefaultHandler == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("DefaultHandler must be set.");
|
||||||
|
}
|
||||||
|
|
||||||
|
routes.Add(new TemplateRoute(routes.DefaultHandler, template, defaults, constraints));
|
||||||
return routes;
|
return routes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Routing
|
||||||
|
{
|
||||||
|
public class RouteConstraintBuilder
|
||||||
|
{
|
||||||
|
public static IDictionary<string, IRouteConstraint>
|
||||||
|
BuildConstraints(IDictionary<string, object> inputConstraints)
|
||||||
|
{
|
||||||
|
if (inputConstraints == null || inputConstraints.Count == 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var constraints = new Dictionary<string, IRouteConstraint>(inputConstraints.Count, StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
foreach (var kvp in inputConstraints)
|
||||||
|
{
|
||||||
|
var constraint = kvp.Value as IRouteConstraint;
|
||||||
|
|
||||||
|
if (constraint == null)
|
||||||
|
{
|
||||||
|
var regexPattern = kvp.Value as string;
|
||||||
|
|
||||||
|
if (regexPattern == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Constraint can be a valid regex string or an IRouteConstraint");
|
||||||
|
}
|
||||||
|
|
||||||
|
var constraintsRegEx = "^(" + regexPattern + ")$";
|
||||||
|
|
||||||
|
constraint = new RegexConstraint(constraintsRegEx);
|
||||||
|
}
|
||||||
|
|
||||||
|
constraints.Add(kvp.Key, constraint);
|
||||||
|
}
|
||||||
|
|
||||||
|
return constraints;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.AspNet.Abstractions;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Routing
|
||||||
|
{
|
||||||
|
public static class RouteConstraintMatcher
|
||||||
|
{
|
||||||
|
public static bool Match([NotNull] IDictionary<string, IRouteConstraint> constraints,
|
||||||
|
[NotNull] IDictionary<string, object> routeValues,
|
||||||
|
[NotNull] HttpContext httpContext,
|
||||||
|
[NotNull] IRouter route,
|
||||||
|
[NotNull] RouteDirection routeDirection)
|
||||||
|
{
|
||||||
|
if (constraints == null)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var kvp in constraints)
|
||||||
|
{
|
||||||
|
var constraint = kvp.Value;
|
||||||
|
if (!constraint.Match(httpContext, route, kvp.Key, routeValues, routeDirection))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
namespace Microsoft.AspNet.Routing
|
||||||
|
{
|
||||||
|
public enum RouteDirection
|
||||||
|
{
|
||||||
|
IncomingRequest,
|
||||||
|
UrlGeneration,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,20 +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 RouteValues : IRouteValues
|
|
||||||
{
|
|
||||||
public RouteValues(IDictionary<string, object> values)
|
|
||||||
{
|
|
||||||
Values = values;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IDictionary<string, object> Values
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
private set;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,27 +2,26 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Abstractions;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Routing.Template
|
namespace Microsoft.AspNet.Routing.Template
|
||||||
{
|
{
|
||||||
public class TemplateRoute : IRouter
|
public class TemplateRoute : IRouter
|
||||||
{
|
{
|
||||||
private readonly IDictionary<string, object> _defaults;
|
private readonly IDictionary<string, object> _defaults;
|
||||||
|
private readonly IDictionary<string, IRouteConstraint> _constraints;
|
||||||
private readonly IRouter _target;
|
private readonly IRouter _target;
|
||||||
private readonly Template _parsedTemplate;
|
|
||||||
private readonly string _routeTemplate;
|
private readonly string _routeTemplate;
|
||||||
private readonly TemplateMatcher _matcher;
|
private readonly TemplateMatcher _matcher;
|
||||||
private readonly TemplateBinder _binder;
|
private readonly TemplateBinder _binder;
|
||||||
|
|
||||||
public TemplateRoute(IRouter target, string routeTemplate)
|
public TemplateRoute(IRouter target, string routeTemplate)
|
||||||
: this(target, routeTemplate, null)
|
: this(target, routeTemplate, null, null)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public TemplateRoute(IRouter target, string routeTemplate, IDictionary<string, object> defaults)
|
public TemplateRoute(IRouter target, string routeTemplate, IDictionary<string, object> defaults,
|
||||||
|
IDictionary<string, object> constraints)
|
||||||
{
|
{
|
||||||
if (target == null)
|
if (target == null)
|
||||||
{
|
{
|
||||||
|
|
@ -32,11 +31,12 @@ namespace Microsoft.AspNet.Routing.Template
|
||||||
_target = target;
|
_target = target;
|
||||||
_routeTemplate = routeTemplate ?? string.Empty;
|
_routeTemplate = routeTemplate ?? string.Empty;
|
||||||
_defaults = defaults ?? new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
_defaults = defaults ?? new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
_constraints = RouteConstraintBuilder.BuildConstraints(constraints);
|
||||||
|
|
||||||
// The parser will throw for invalid routes.
|
// The parser will throw for invalid routes.
|
||||||
_parsedTemplate = TemplateParser.Parse(RouteTemplate);
|
var parsedTemplate = TemplateParser.Parse(RouteTemplate);
|
||||||
|
|
||||||
_matcher = new TemplateMatcher(_parsedTemplate);
|
_matcher = new TemplateMatcher(parsedTemplate);
|
||||||
_binder = new TemplateBinder(_parsedTemplate, _defaults);
|
_binder = new TemplateBinder(_parsedTemplate, _defaults);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -74,7 +74,14 @@ namespace Microsoft.AspNet.Routing.Template
|
||||||
// Not currently doing anything to clean this up if it's not a match. Consider hardening this.
|
// Not currently doing anything to clean this up if it's not a match. Consider hardening this.
|
||||||
context.Values = values;
|
context.Values = values;
|
||||||
|
|
||||||
await _target.RouteAsync(context);
|
if (RouteConstraintMatcher.Match(_constraints,
|
||||||
|
values,
|
||||||
|
context.HttpContext,
|
||||||
|
this,
|
||||||
|
RouteDirection.IncomingRequest))
|
||||||
|
{
|
||||||
|
await _target.RouteAsync(context);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
{
|
{
|
||||||
"version": "0.1-alpha-*",
|
"version": "0.1-alpha-*",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.AspNet.Abstractions": "0.1-alpha-*"
|
"Microsoft.AspNet.Abstractions": "0.1-alpha-*",
|
||||||
|
"Microsoft.AspNet.DependencyInjection" : "0.1-alpha-*"
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"net45": {},
|
"net45": {},
|
||||||
|
|
@ -19,6 +20,9 @@
|
||||||
"System.Runtime": "4.0.20.0",
|
"System.Runtime": "4.0.20.0",
|
||||||
"System.Runtime.Extensions": "4.0.10.0",
|
"System.Runtime.Extensions": "4.0.10.0",
|
||||||
"System.Text.RegularExpressions": "4.0.0.0",
|
"System.Text.RegularExpressions": "4.0.0.0",
|
||||||
|
"System.Runtime.Hosting": "3.9.0.0",
|
||||||
|
"System.Runtime.InteropServices": "4.0.10.0",
|
||||||
|
"System.Text.Encoding": "4.0.10.0",
|
||||||
"System.Threading.Tasks": "4.0.0.0"
|
"System.Threading.Tasks": "4.0.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -196,7 +196,7 @@ namespace Microsoft.AspNet.Routing.Template.Tests
|
||||||
|
|
||||||
private static TemplateRoute CreateRoute(string template, object defaults, bool accept = true)
|
private static TemplateRoute CreateRoute(string template, object defaults, bool accept = true)
|
||||||
{
|
{
|
||||||
return new TemplateRoute(CreateTarget(accept), template, new RouteValueDictionary(defaults));
|
return new TemplateRoute(CreateTarget(accept), template, new RouteValueDictionary(defaults), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IRouter CreateTarget(bool accept = true)
|
private static IRouter CreateTarget(bool accept = true)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue